API naming for LG (#1883)
* rename lg * rename templates property to items * rename * rename * Updated Templates to look like a collection * Fixed unit tests Co-authored-by: Steven Ickman <stevenic@microsoft.com>
This commit is contained in:
Родитель
4d5b305515
Коммит
42e366d9ea
|
@ -0,0 +1,618 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import { Activity, SuggestedActions, Attachment, ActivityTypes, ActionTypes, CardAction } from 'botframework-schema';
|
||||
import { MessageFactory } from './messageFactory';
|
||||
import { CardFactory } from './cardFactory';
|
||||
|
||||
/**
|
||||
* The ActivityFactory
|
||||
* to generate text and then uses simple markdown semantics like chatdown to create Activity.
|
||||
*/
|
||||
export class ActivityFactory {
|
||||
|
||||
private static readonly lgType = 'lgType';
|
||||
private static readonly errorPrefix = '[ERROR]';
|
||||
private static readonly warningPrefix = '[WARNING]';
|
||||
private static adaptiveCardType: string = CardFactory.contentTypes.adaptiveCard;
|
||||
|
||||
private static readonly genericCardTypeMapping: Map<string, string> = new Map<string, string>
|
||||
([
|
||||
[ 'herocard', CardFactory.contentTypes.heroCard ],
|
||||
[ 'thumbnailcard', CardFactory.contentTypes.thumbnailCard ],
|
||||
[ 'audiocard', CardFactory.contentTypes.audioCard ],
|
||||
[ 'videocard', CardFactory.contentTypes.videoCard ],
|
||||
[ 'animationcard', CardFactory.contentTypes.animationCard ],
|
||||
[ 'signincard', CardFactory.contentTypes.signinCard ],
|
||||
[ 'oauthcard', CardFactory.contentTypes.oauthCard ],
|
||||
[ 'receiptcard', CardFactory.contentTypes.receiptCard ],
|
||||
]);
|
||||
|
||||
private static readonly activityProperties: string[] = ['type','id','timestamp','localTimestamp','localTimezone','callerId',
|
||||
'serviceUrl','channelId','from','conversation','recipient','textFormat','attachmentLayout','membersAdded',
|
||||
'membersRemoved','reactionsAdded','reactionsRemoved','topicName','historyDisclosed','locale','text','speak',
|
||||
'inputHint','summary','suggestedActions','attachments','entities','channelData','action','replyToId','label',
|
||||
'valueType','value','name','typrelatesToe','code','expiration','importance','deliveryMode','listenFor',
|
||||
'textHighlights','semanticAction'];
|
||||
|
||||
private static readonly cardActionProperties: string[] = ['type','title','image','text','displayText','value','channelData'];
|
||||
|
||||
/**
|
||||
* Generate the activity.
|
||||
* @param lgResult string result from languageGenerator.
|
||||
*/
|
||||
public static fromObject(lgResult: any): Partial<Activity> {
|
||||
const diagnostics: string[] = this.checkLGResult(lgResult);
|
||||
const errors: string[] = diagnostics.filter((u: string): boolean => u.startsWith(this.errorPrefix));
|
||||
if (errors !== undefined && errors.length > 0) {
|
||||
throw new Error(`${ errors.join('\n') }`);
|
||||
}
|
||||
|
||||
if (typeof lgResult === 'string') {
|
||||
const structuredLGResult: any = this.parseStructuredLGResult(lgResult.trim());
|
||||
return structuredLGResult === undefined ?
|
||||
this.buildActivityFromText(lgResult.trim())
|
||||
:this.buildActivityFromLGStructuredResult(lgResult);
|
||||
}
|
||||
|
||||
return this.buildActivityFromLGStructuredResult(lgResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* check the LG result before generate an Activity.
|
||||
* @param lgResult lg output.
|
||||
* @returns Diagnostic list.
|
||||
*/
|
||||
public static checkLGResult(lgResult: any): string[] {
|
||||
if (lgResult === undefined) {
|
||||
return [this.buildDiagnostic('LG output is empty', false)];
|
||||
}
|
||||
|
||||
if (typeof lgResult === 'string') {
|
||||
if (!lgResult.startsWith('{') || !lgResult.endsWith('}')) {
|
||||
return [this.buildDiagnostic('LG output is not a json object, and will fallback to string format.', false)];
|
||||
}
|
||||
|
||||
let lgStructuredResult: any = undefined;
|
||||
|
||||
try {
|
||||
lgStructuredResult = JSON.parse(lgResult);
|
||||
} catch (error) {
|
||||
return [this.buildDiagnostic('LG output is not a json object, and will fallback to string format.', false)];
|
||||
}
|
||||
|
||||
return this.checkStructuredResult(lgStructuredResult);
|
||||
} else {
|
||||
return this.checkStructuredResult(lgResult);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a lg result, create a text activity. This method will create a MessageActivity from text.
|
||||
* @param text lg text output.
|
||||
*/
|
||||
private static buildActivityFromText(text: string): Partial<Activity> {
|
||||
const msg: Partial<Activity> = {
|
||||
type: ActivityTypes.Message,
|
||||
text: text,
|
||||
speak: text
|
||||
};
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a structured lg result, create an activity. This method will create an MessageActivity from object
|
||||
* @param lgValue lg output.
|
||||
*/
|
||||
private static buildActivityFromLGStructuredResult(lgValue: any): Partial<Activity> {
|
||||
let activity: Partial<Activity> = {};
|
||||
|
||||
const type: string = this.getStructureType(lgValue);
|
||||
if (this.genericCardTypeMapping.has(type) || type === 'attachment') {
|
||||
activity = MessageFactory.attachment(this.getAttachment(lgValue));
|
||||
} else if (type === 'activity') {
|
||||
activity = this.buildActivity(lgValue);
|
||||
}
|
||||
|
||||
return activity;
|
||||
}
|
||||
|
||||
private static buildActivity(messageValue: any): Partial<Activity> {
|
||||
let activity: Partial<Activity> = { type: ActivityTypes.Message };
|
||||
for (const key of Object.keys(messageValue)) {
|
||||
const property: string = key.trim();
|
||||
if (property === this.lgType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const value: any = messageValue[key];
|
||||
|
||||
switch (property.toLowerCase()) {
|
||||
case 'attachments':
|
||||
activity.attachments = this.getAttachments(value);
|
||||
break;
|
||||
case 'suggestedactions':
|
||||
activity.suggestedActions = this.getSuggestions(value);
|
||||
break;
|
||||
default:
|
||||
var properties = this.activityProperties.map((u: string): string => u.toLowerCase());
|
||||
if (properties.includes(property.toLowerCase()))
|
||||
{
|
||||
var realPropertyName = this.activityProperties[properties.indexOf(property.toLowerCase())];
|
||||
activity[realPropertyName] = value;
|
||||
} else {
|
||||
activity[property.toLowerCase()] = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return activity;
|
||||
}
|
||||
|
||||
private static getSuggestions(suggestionsValue: any): SuggestedActions {
|
||||
const actions: any[] = this.normalizedToList(suggestionsValue);
|
||||
|
||||
const suggestedActions: SuggestedActions = {
|
||||
actions : this.getCardActions(actions),
|
||||
to: []
|
||||
};
|
||||
|
||||
return suggestedActions;
|
||||
}
|
||||
|
||||
private static getButtons(buttonsValue: any): CardAction[] {
|
||||
const actions: any[] = this.normalizedToList(buttonsValue);
|
||||
return this.getCardActions(actions);
|
||||
}
|
||||
|
||||
private static getCardActions(actions: any[]): CardAction[] {
|
||||
return actions.map((u: any): CardAction => this.getCardAction(u));
|
||||
}
|
||||
|
||||
private static getCardAction(action: any): CardAction
|
||||
{
|
||||
let cardAction: CardAction;
|
||||
if (typeof action === 'string') {
|
||||
cardAction = { type: ActionTypes.ImBack, value: action, title: action, channelData: undefined };
|
||||
} else {
|
||||
const type: string = this.getStructureType(action);
|
||||
cardAction = {
|
||||
type: ActionTypes.ImBack,
|
||||
title: '',
|
||||
value: ''
|
||||
};
|
||||
|
||||
if (type === 'cardaction') {
|
||||
for (const key of Object.keys(action)) {
|
||||
const property: string = key.trim();
|
||||
if (property === this.lgType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const value: any = action[key];
|
||||
|
||||
switch (property.toLowerCase()) {
|
||||
case 'displaytext':
|
||||
cardAction.displayText = value;
|
||||
break;
|
||||
case 'channeldata':
|
||||
cardAction.channelData = value;
|
||||
break;
|
||||
default:
|
||||
cardAction[property.toLowerCase()] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cardAction;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private static getAttachments(input: any): Attachment[] {
|
||||
const attachments: Attachment[] = [];
|
||||
const attachmentsJsonList: any[] = this.normalizedToList(input);
|
||||
|
||||
for (const attachmentsJson of attachmentsJsonList) {
|
||||
if (typeof attachmentsJson === 'object') {
|
||||
attachments.push(this.getAttachment(attachmentsJson));
|
||||
}
|
||||
}
|
||||
|
||||
return attachments;
|
||||
}
|
||||
|
||||
private static getAttachment(input: any): Attachment {
|
||||
let attachment: Attachment = {
|
||||
contentType: ''
|
||||
};
|
||||
const type: string = this.getStructureType(input);
|
||||
if (this.genericCardTypeMapping.has(type)) {
|
||||
attachment = this.getCardAttachment(this.genericCardTypeMapping.get(type), input);
|
||||
} else if (type === 'adaptivecard') {
|
||||
attachment = CardFactory.adaptiveCard(input);
|
||||
} else if (type === 'attachment') {
|
||||
attachment = this.getNormalAttachment(input);
|
||||
} else {
|
||||
attachment = {contentType: type, content: input};
|
||||
}
|
||||
|
||||
return attachment;
|
||||
}
|
||||
|
||||
private static getNormalAttachment(input: any): Attachment {
|
||||
const attachment: Attachment = {contentType:''};
|
||||
|
||||
for (const key of Object.keys(input)) {
|
||||
const property: string = key.trim();
|
||||
const value: any = input[key];
|
||||
|
||||
switch (property.toLowerCase()) {
|
||||
case 'contenttype':
|
||||
const type: string = value.toString().toLowerCase();
|
||||
if (this.genericCardTypeMapping.has(type)) {
|
||||
attachment.contentType = this.genericCardTypeMapping.get(type);
|
||||
} else if (type === 'adaptivecard') {
|
||||
attachment.contentType = this.adaptiveCardType;
|
||||
} else {
|
||||
attachment.contentType = type;
|
||||
}
|
||||
break;
|
||||
case 'contenturl':
|
||||
attachment.contentUrl = value;
|
||||
break;
|
||||
case 'thumbnailurl':
|
||||
attachment.thumbnailUrl = value;
|
||||
break;
|
||||
default:
|
||||
attachment[property.toLowerCase()] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return attachment;
|
||||
}
|
||||
|
||||
private static getCardAttachment(type: string, input: any): Attachment {
|
||||
const card: any = {};
|
||||
|
||||
for (const key of Object.keys(input)) {
|
||||
const property: string = key.trim().toLowerCase();
|
||||
const value: any = input[key];
|
||||
|
||||
switch (property) {
|
||||
case 'tap':
|
||||
card[property] = this.getCardAction(value);
|
||||
break;
|
||||
case 'image':
|
||||
case 'images':
|
||||
if (type === CardFactory.contentTypes.heroCard || type === CardFactory.contentTypes.thumbnailCard) {
|
||||
if (!('images' in card)) {
|
||||
card['images'] = [];
|
||||
}
|
||||
|
||||
const imageList: string[] = this.normalizedToList(value).map((u): string => u.toString());
|
||||
imageList.forEach( (u): any => card['images'].push({url : u}));
|
||||
} else {
|
||||
card['image'] = {url: value.toString()};
|
||||
}
|
||||
break;
|
||||
case 'media':
|
||||
if (!('media' in card)) {
|
||||
card['media'] = [];
|
||||
}
|
||||
|
||||
const mediaList: string[] = this.normalizedToList(value).map((u): string => u.toString());
|
||||
mediaList.forEach( (u): any => card['media'].push({url : u}));
|
||||
break;
|
||||
case 'buttons':
|
||||
if (!('buttons' in card)) {
|
||||
card['buttons'] = [];
|
||||
}
|
||||
|
||||
const buttons: any[] = this.getButtons(value);
|
||||
buttons.forEach( (u): any => card[property].push(u));
|
||||
break;
|
||||
case 'autostart':
|
||||
case 'shareable':
|
||||
case 'autoloop':
|
||||
const boolValue: boolean = this.getValidBooleanValue(value.toString());
|
||||
if (boolValue !== undefined) {
|
||||
card[property] = boolValue;
|
||||
}
|
||||
break;
|
||||
case 'connectionname':
|
||||
card['connectionName'] = value;
|
||||
break;
|
||||
default:
|
||||
card[property.toLowerCase()] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const attachment: Attachment = {
|
||||
contentType: type,
|
||||
content: card
|
||||
};
|
||||
|
||||
return attachment;
|
||||
}
|
||||
|
||||
private static normalizedToList(item: any): any[] {
|
||||
if (item === undefined) {
|
||||
return [];
|
||||
} else if (Array.isArray(item)) {
|
||||
return item;
|
||||
} else {
|
||||
return [item];
|
||||
}
|
||||
}
|
||||
|
||||
private static parseStructuredLGResult(lgStringResult: string): any
|
||||
{
|
||||
let lgStructuredResult: any = undefined;
|
||||
if (lgStringResult === undefined || lgStringResult === '') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
lgStringResult = lgStringResult.trim();
|
||||
|
||||
if (lgStringResult === '' || !lgStringResult.startsWith('{') || !lgStringResult.endsWith('}')) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
lgStructuredResult = JSON.parse(lgStringResult);
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return lgStructuredResult;
|
||||
}
|
||||
|
||||
private static checkStructuredResult(input: any): string[] {
|
||||
const result: string[] = [];
|
||||
const type: string = this.getStructureType(input);
|
||||
if (this.genericCardTypeMapping.has(type) || type === 'attachment') {
|
||||
result.push(...this.checkAttachment(input));
|
||||
} else if (type === 'activity') {
|
||||
result.push(...this.checkActivity(input));
|
||||
} else {
|
||||
const diagnosticMessage: string = (type === undefined || type === '') ?
|
||||
`'${ this.lgType }' does not exist in lg output json object.`
|
||||
: `Type '${ type }' is not supported currently.`;
|
||||
result.push(this.buildDiagnostic(diagnosticMessage));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static checkActivity(input: any): string[] {
|
||||
const result: string[] = [];
|
||||
let activityType: string = undefined;
|
||||
if ('type' in input) {
|
||||
activityType = input['type'].toString().trim();
|
||||
}
|
||||
|
||||
result.push(...this.checkActivityType(activityType));
|
||||
result.push(...this.checkActivityPropertyName(input));
|
||||
result.push(...this.checkActivityProperties(input));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static checkActivityType(activityType: string): string[] {
|
||||
if (activityType !== undefined) {
|
||||
if (!Object.values(ActivityTypes).map((u: string): string => u.toLowerCase()).includes(activityType.toLowerCase())) {
|
||||
return [this.buildDiagnostic(`'${ activityType }' is not a valid activity type.`)];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
private static checkActivityPropertyName(input: any): string[] {
|
||||
const invalidProperties: string[] = [];
|
||||
for (const property of Object.keys(input)) {
|
||||
if (property === this.lgType) {
|
||||
continue;
|
||||
}
|
||||
if (!this.activityProperties.map((u: string): string => u.toLowerCase()).includes(property.toLowerCase())) {
|
||||
invalidProperties.push(property);
|
||||
}
|
||||
}
|
||||
if (invalidProperties.length > 0) {
|
||||
return [this.buildDiagnostic(`'${ invalidProperties.join(',') }' not support in Activity.`, false)];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private static checkActivityProperties(input: any): string[] {
|
||||
const result: string[] = [];
|
||||
for (const key of Object.keys(input)) {
|
||||
const property: string = key.trim();
|
||||
const value: any = input[key];
|
||||
|
||||
switch (property.toLowerCase()) {
|
||||
case 'attachments':
|
||||
result.push(...this.checkAttachments(value));
|
||||
break;
|
||||
case 'suggestedactions':
|
||||
result.push(...this.checkSuggestions(value));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static checkSuggestions(value: any): string[] {
|
||||
const actions: any[] = this.normalizedToList(value);
|
||||
return this.checkCardActions(actions);
|
||||
}
|
||||
|
||||
private static checkButtons(value: any): string[] {
|
||||
const actions: any[] = this.normalizedToList(value);
|
||||
return this.checkCardActions(actions);
|
||||
}
|
||||
|
||||
private static checkCardActions(actions: any[]): string[] {
|
||||
const result: string[] = [];
|
||||
actions.forEach((u: any): void => { result.push(...this.checkCardAction(u)); });
|
||||
return result;
|
||||
}
|
||||
|
||||
private static checkCardAction(value: any): string[] {
|
||||
const result: string[] = [];
|
||||
if (typeof value === 'string') {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (typeof value === 'object') {
|
||||
const type: string = this.getStructureType(value);
|
||||
if (type !== 'cardaction') {
|
||||
result.push(this.buildDiagnostic(`'${ type }' is not card action type.`, false));
|
||||
} else {
|
||||
result.push(...this.checkCardActionPropertyName(value));
|
||||
if ('type' in value) {
|
||||
result.push(...this.checkCardActionType(value['type']));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result.push(this.buildDiagnostic(`'${ value }' is not a valid card action format.`, false));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static checkCardActionPropertyName(input: any): string[] {
|
||||
const invalidProperties: string[] = [];
|
||||
for (const property of Object.keys(input)) {
|
||||
if (property === this.lgType) {
|
||||
continue;
|
||||
}
|
||||
if (!this.cardActionProperties.map((u: string): string => u.toLowerCase()).includes(property.toLowerCase())) {
|
||||
invalidProperties.push(property);
|
||||
}
|
||||
}
|
||||
if (invalidProperties.length > 0) {
|
||||
return [this.buildDiagnostic(`'${ invalidProperties.join(',') }' not support in card action.`, false)];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private static checkCardActionType(cardActionType: string): string[] {
|
||||
const result: string[] = [];
|
||||
if (!cardActionType) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!Object.values(ActionTypes).map((u: string): string => u.toLowerCase()).includes(cardActionType.toLowerCase())) {
|
||||
return [this.buildDiagnostic(`'${ cardActionType }' is not a valid card action type.`)];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static checkAttachments(value: any): string[] {
|
||||
const result: string[] = [];
|
||||
const attachmentsJsonList: any[] = this.normalizedToList(value);
|
||||
|
||||
for (const attachmentsJson of attachmentsJsonList) {
|
||||
if (typeof attachmentsJson === 'object') {
|
||||
result.push(...this.checkAttachment(attachmentsJson));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static checkAttachment(value: any): string[] {
|
||||
const result: string[] = [];
|
||||
const type: string = this.getStructureType(value);
|
||||
if (this.genericCardTypeMapping.has(type)) {
|
||||
result.push(...this.checkCardAttachment(value));
|
||||
} else if (type === 'adaptivecard') {
|
||||
// TODO
|
||||
// check adaptivecard format
|
||||
} else if (type === 'attachment') {
|
||||
// TODO
|
||||
// Check attachment format
|
||||
} else {
|
||||
result.push(this.buildDiagnostic(`'${ type }' is not an attachment type.`, false));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static checkCardAttachment(input: any): string[] {
|
||||
const result: string[] = [];
|
||||
for (const key of Object.keys(input)) {
|
||||
const property: string = key.trim().toLowerCase();
|
||||
const value: any = input[key];
|
||||
|
||||
switch (property) {
|
||||
case 'buttons':
|
||||
result.push(...this.checkButtons(value));
|
||||
break;
|
||||
case 'autostart':
|
||||
case 'shareable':
|
||||
case 'autoloop':
|
||||
const boolValue: boolean = this.getValidBooleanValue(value.toString());
|
||||
if (boolValue === undefined) {
|
||||
result.push(this.buildDiagnostic(`'${ value.toString() }' is not a boolean value.`));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static getStructureType(input: any): string {
|
||||
let result = '';
|
||||
|
||||
if (input && typeof input === 'object') {
|
||||
if (this.lgType in input) {
|
||||
result = input[this.lgType].toString();
|
||||
} else if ('type' in input) {
|
||||
// Adaptive card type
|
||||
result = input['type'].toString();
|
||||
}
|
||||
}
|
||||
|
||||
return result.trim().toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
private static getValidBooleanValue(boolValue: string): boolean{
|
||||
if (boolValue.toLowerCase() === 'true')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (boolValue.toLowerCase() === 'false')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private static buildDiagnostic(message: string, isError: boolean = true): string {
|
||||
message = message === undefined ? '' : message;
|
||||
return isError ? this.errorPrefix + message : this.warningPrefix + message;
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
export * from 'botframework-schema';
|
||||
export * from './activityFactory';
|
||||
export * from './appCredentials';
|
||||
export * from './activityHandler';
|
||||
export * from './activityHandlerBase';
|
||||
|
|
|
@ -94,19 +94,19 @@ For NodeJS, add botbuilder-lg
|
|||
Load the template manager with your .lg file
|
||||
|
||||
```typescript
|
||||
let lgFile = new LGParser.parseFile(filePath, importResolver?, expressionParser?);
|
||||
let templates = new Templates.parseFile(filePath, importResolver?, expressionParser?);
|
||||
```
|
||||
|
||||
When you need template expansion, call the LGFile and pass in the relevant template name
|
||||
When you need template expansion, call the templates and pass in the relevant template name
|
||||
|
||||
```typescript
|
||||
await turnContext.sendActivity(lgFile.evaluateTemplate("<TemplateName>", entitiesCollection));
|
||||
await turnContext.sendActivity(templates.evaluate("<TemplateName>", entitiesCollection));
|
||||
```
|
||||
|
||||
If your template needs specific entity values to be passed for resolution/ expansion, you can pass them in on the call to `evaluateTemplate`
|
||||
|
||||
```typescript
|
||||
await turnContext.sendActivity(lgFile.evaluateTemplate("WordGameReply", { GameName = "MarcoPolo" } ));
|
||||
await turnContext.sendActivity(templates.evaluate("WordGameReply", { GameName = "MarcoPolo" } ));
|
||||
```
|
||||
|
||||
[1]:https://github.com/Microsoft/BotBuilder/blob/master/specs/botframework-activity/botframework-activity.md
|
||||
|
|
|
@ -1,320 +0,0 @@
|
|||
/**
|
||||
* @module botbuilder-lg
|
||||
*/
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import { ActivityTypes, ActionTypes } from 'botbuilder-core';
|
||||
import { CardFactory } from 'botbuilder-core';
|
||||
import { Diagnostic, DiagnosticSeverity } from './diagnostic';
|
||||
import { Range } from './range';
|
||||
import { Position } from './position';
|
||||
import { Evaluator } from './evaluator';
|
||||
|
||||
/**
|
||||
* Structure LG result checker.
|
||||
*/
|
||||
export class ActivityChecker {
|
||||
public static readonly genericCardTypeMapping: Map<string, string> = new Map<string, string>
|
||||
([
|
||||
[ 'herocard', CardFactory.contentTypes.heroCard ],
|
||||
[ 'thumbnailcard', CardFactory.contentTypes.thumbnailCard ],
|
||||
[ 'audiocard', CardFactory.contentTypes.audioCard ],
|
||||
[ 'videocard', CardFactory.contentTypes.videoCard ],
|
||||
[ 'animationcard', CardFactory.contentTypes.animationCard ],
|
||||
[ 'signincard', CardFactory.contentTypes.signinCard ],
|
||||
[ 'oauthcard', CardFactory.contentTypes.oauthCard ],
|
||||
[ 'receiptcard', CardFactory.contentTypes.receiptCard ],
|
||||
]);
|
||||
|
||||
public static readonly activityProperties: string[] = ['type','id','timestamp','localTimestamp','localTimezone','callerId',
|
||||
'serviceUrl','channelId','from','conversation','recipient','textFormat','attachmentLayout','membersAdded',
|
||||
'membersRemoved','reactionsAdded','reactionsRemoved','topicName','historyDisclosed','locale','text','speak',
|
||||
'inputHint','summary','suggestedActions','attachments','entities','channelData','action','replyToId','label',
|
||||
'valueType','value','name','typrelatesToe','code','expiration','importance','deliveryMode','listenFor',
|
||||
'textHighlights','semanticAction'];
|
||||
|
||||
public static readonly cardActionProperties: string[] = ['type','title','image','text','displayText','value','channelData'];
|
||||
|
||||
/**
|
||||
* check the LG result before generate an Activity.
|
||||
* @param lgResult lg output.
|
||||
* @returns Diagnostic list.
|
||||
*/
|
||||
public static check(lgResult: any): Diagnostic[] {
|
||||
if (lgResult === undefined) {
|
||||
return [this.buildDiagnostic('LG output is empty', false)];
|
||||
}
|
||||
|
||||
if (typeof lgResult === 'string') {
|
||||
if (!lgResult.startsWith('{') || !lgResult.endsWith('}')) {
|
||||
return [this.buildDiagnostic('LG output is not a json object, and will fallback to string format.', false)];
|
||||
}
|
||||
|
||||
let lgStructuredResult: any = undefined;
|
||||
|
||||
try {
|
||||
lgStructuredResult = JSON.parse(lgResult);
|
||||
} catch (error) {
|
||||
return [this.buildDiagnostic('LG output is not a json object, and will fallback to string format.', false)];
|
||||
}
|
||||
|
||||
return this.checkStructuredResult(lgStructuredResult);
|
||||
} else {
|
||||
return this.checkStructuredResult(lgResult);
|
||||
}
|
||||
}
|
||||
|
||||
public static checkStructuredResult(input: any): Diagnostic[] {
|
||||
const result: Diagnostic[] = [];
|
||||
const type: string = this.getStructureType(input);
|
||||
if (ActivityChecker.genericCardTypeMapping.has(type) || type === 'attachment') {
|
||||
result.push(...this.checkAttachment(input));
|
||||
} else if (type === 'activity') {
|
||||
result.push(...this.checkActivity(input));
|
||||
} else {
|
||||
const diagnosticMessage: string = (type === undefined || type === '') ?
|
||||
`'${ Evaluator.LGType }' does not exist in lg output json object.`
|
||||
: `Type '${ type }' is not supported currently.`;
|
||||
result.push(this.buildDiagnostic(diagnosticMessage));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static checkActivity(input: any): Diagnostic[] {
|
||||
const result: Diagnostic[] = [];
|
||||
let activityType: string = undefined;
|
||||
if ('type' in input) {
|
||||
activityType = input['type'].toString().trim();
|
||||
}
|
||||
|
||||
result.push(...this.checkActivityType(activityType));
|
||||
result.push(...this.checkActivityPropertyName(input));
|
||||
result.push(...this.checkActivityProperties(input));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static checkActivityType(activityType: string): Diagnostic[] {
|
||||
if (activityType !== undefined) {
|
||||
if (!Object.values(ActivityTypes).map((u: string): string => u.toLowerCase()).includes(activityType.toLowerCase())) {
|
||||
return [this.buildDiagnostic(`'${ activityType }' is not a valid activity type.`)];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
private static checkActivityPropertyName(input: any): Diagnostic[] {
|
||||
const invalidProperties: string[] = [];
|
||||
for (const property of Object.keys(input)) {
|
||||
if (property === Evaluator.LGType) {
|
||||
continue;
|
||||
}
|
||||
if (!ActivityChecker.activityProperties.map((u: string): string => u.toLowerCase()).includes(property.toLowerCase())) {
|
||||
invalidProperties.push(property);
|
||||
}
|
||||
}
|
||||
if (invalidProperties.length > 0) {
|
||||
return [this.buildDiagnostic(`'${ invalidProperties.join(',') }' not support in Activity.`, false)];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private static checkActivityProperties(input: any): Diagnostic[] {
|
||||
const result: Diagnostic[] = [];
|
||||
for (const key of Object.keys(input)) {
|
||||
const property: string = key.trim();
|
||||
const value: any = input[key];
|
||||
|
||||
switch (property.toLowerCase()) {
|
||||
case 'attachments':
|
||||
result.push(...this.checkAttachments(value));
|
||||
break;
|
||||
case 'suggestedactions':
|
||||
result.push(...this.checkSuggestions(value));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static checkSuggestions(value: any): Diagnostic[] {
|
||||
const actions: any[] = this.normalizedToList(value);
|
||||
return this.checkCardActions(actions);
|
||||
}
|
||||
|
||||
private static checkButtons(value: any): Diagnostic[] {
|
||||
const actions: any[] = this.normalizedToList(value);
|
||||
return this.checkCardActions(actions);
|
||||
}
|
||||
|
||||
private static checkCardActions(actions: any[]): Diagnostic[] {
|
||||
const result: Diagnostic[] = [];
|
||||
actions.forEach((u: any): void => { result.push(...this.checkCardAction(u)); });
|
||||
return result;
|
||||
}
|
||||
|
||||
private static checkCardAction(value: any): Diagnostic[] {
|
||||
const result: Diagnostic[] = [];
|
||||
if (typeof value === 'string') {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (typeof value === 'object') {
|
||||
const type: string = this.getStructureType(value);
|
||||
if (type !== 'cardaction') {
|
||||
result.push(this.buildDiagnostic(`'${ type }' is not card action type.`, false));
|
||||
} else {
|
||||
result.push(...this.checkCardActionPropertyName(value));
|
||||
if ('type' in value) {
|
||||
result.push(...this.checkCardActionType(value['type']));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result.push(this.buildDiagnostic(`'${ value }' is not a valid card action format.`, false));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static checkCardActionPropertyName(input: any): Diagnostic[] {
|
||||
const invalidProperties: string[] = [];
|
||||
for (const property of Object.keys(input)) {
|
||||
if (property === Evaluator.LGType) {
|
||||
continue;
|
||||
}
|
||||
if (!ActivityChecker.cardActionProperties.map((u: string): string => u.toLowerCase()).includes(property.toLowerCase())) {
|
||||
invalidProperties.push(property);
|
||||
}
|
||||
}
|
||||
if (invalidProperties.length > 0) {
|
||||
return [this.buildDiagnostic(`'${ invalidProperties.join(',') }' not support in card action.`, false)];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private static checkCardActionType(cardActionType: string): Diagnostic[] {
|
||||
const result: Diagnostic[] = [];
|
||||
if (!cardActionType) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!Object.values(ActionTypes).map((u: string): string => u.toLowerCase()).includes(cardActionType.toLowerCase())) {
|
||||
return [this.buildDiagnostic(`'${ cardActionType }' is not a valid card action type.`)];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static checkAttachments(value: any): Diagnostic[] {
|
||||
const result: Diagnostic[] = [];
|
||||
const attachmentsJsonList: any[] = this.normalizedToList(value);
|
||||
|
||||
for (const attachmentsJson of attachmentsJsonList) {
|
||||
if (typeof attachmentsJson === 'object') {
|
||||
result.push(...this.checkAttachment(attachmentsJson));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static checkAttachment(value: any): Diagnostic[] {
|
||||
const result: Diagnostic[] = [];
|
||||
const type: string = this.getStructureType(value);
|
||||
if (ActivityChecker.genericCardTypeMapping.has(type)) {
|
||||
result.push(...this.checkCardAttachment(value));
|
||||
} else if (type === 'adaptivecard') {
|
||||
// TODO
|
||||
// check adaptivecard format
|
||||
} else if (type === 'attachment') {
|
||||
// TODO
|
||||
// Check attachment format
|
||||
} else {
|
||||
result.push(this.buildDiagnostic(`'${ type }' is not an attachment type.`, false));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static checkCardAttachment(input: any): Diagnostic[] {
|
||||
const result: Diagnostic[] = [];
|
||||
for (const key of Object.keys(input)) {
|
||||
const property: string = key.trim().toLowerCase();
|
||||
const value: any = input[key];
|
||||
|
||||
switch (property) {
|
||||
case 'buttons':
|
||||
result.push(...this.checkButtons(value));
|
||||
break;
|
||||
case 'autostart':
|
||||
case 'shareable':
|
||||
case 'autoloop':
|
||||
const boolValue: boolean = this.getValidBooleanValue(value.toString());
|
||||
if (boolValue === undefined) {
|
||||
result.push(this.buildDiagnostic(`'${ value.toString() }' is not a boolean value.`));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static getStructureType(input: any): string {
|
||||
let result = '';
|
||||
|
||||
if (input !== undefined) {
|
||||
if (Evaluator.LGType in input) {
|
||||
result = input[Evaluator.LGType].toString();
|
||||
} else if ('type' in input) {
|
||||
// Adaptive card type
|
||||
result = input['type'].toString();
|
||||
}
|
||||
}
|
||||
|
||||
return result.trim().toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
private static getValidBooleanValue(boolValue: string): boolean{
|
||||
if (boolValue.toLowerCase() === 'true')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (boolValue.toLowerCase() === 'false')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private static buildDiagnostic(message: string, isError: boolean = true): Diagnostic {
|
||||
message = message === undefined ? '' : message;
|
||||
const emptyRange: Range = new Range(new Position(0, 0), new Position(0, 0));
|
||||
return isError ? new Diagnostic(emptyRange, message, DiagnosticSeverity.Error)
|
||||
: new Diagnostic(emptyRange, message, DiagnosticSeverity.Warning);
|
||||
}
|
||||
|
||||
private static normalizedToList(item: any): any[] {
|
||||
if (item === undefined) {
|
||||
return [];
|
||||
} else if (Array.isArray(item)) {
|
||||
return item;
|
||||
} else {
|
||||
return [item];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,350 +0,0 @@
|
|||
/**
|
||||
* @module botbuilder-lg
|
||||
*/
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import { Activity, SuggestedActions, Attachment, ActivityTypes, ActionTypes, CardAction } from 'botframework-schema';
|
||||
import { MessageFactory, CardFactory } from 'botbuilder-core';
|
||||
import { Diagnostic, DiagnosticSeverity } from './diagnostic';
|
||||
import { ActivityChecker } from './activityChecker';
|
||||
import { Evaluator } from './evaluator';
|
||||
|
||||
/**
|
||||
* The ActivityFactory
|
||||
* to generate text and then uses simple markdown semantics like chatdown to create Activity.
|
||||
*/
|
||||
export class ActivityFactory {
|
||||
private static adaptiveCardType: string = CardFactory.contentTypes.adaptiveCard;
|
||||
|
||||
/**
|
||||
* Generate the activity.
|
||||
* @param lgResult string result from languageGenerator.
|
||||
*/
|
||||
public static createActivity(lgResult: any): Partial<Activity> {
|
||||
const diagnostics: Diagnostic[] = ActivityChecker.check(lgResult);
|
||||
const errors: Diagnostic[] = diagnostics.filter((u: Diagnostic): boolean => u.severity === DiagnosticSeverity.Error);
|
||||
if (errors !== undefined && errors.length > 0) {
|
||||
throw new Error(`${ errors.join('\n') }`);
|
||||
}
|
||||
|
||||
if (typeof lgResult === 'string') {
|
||||
const structuredLGResult: any = this.parseStructuredLGResult(lgResult.trim());
|
||||
return structuredLGResult === undefined ?
|
||||
this.buildActivityFromText(lgResult.trim())
|
||||
:this.buildActivityFromLGStructuredResult(lgResult);
|
||||
}
|
||||
|
||||
return this.buildActivityFromLGStructuredResult(lgResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a lg result, create a text activity. This method will create a MessageActivity from text.
|
||||
* @param text lg text output.
|
||||
*/
|
||||
private static buildActivityFromText(text: string): Partial<Activity> {
|
||||
return MessageFactory.text(text, text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a structured lg result, create an activity. This method will create an MessageActivity from object
|
||||
* @param lgValue lg output.
|
||||
*/
|
||||
private static buildActivityFromLGStructuredResult(lgValue: any): Partial<Activity> {
|
||||
let activity: Partial<Activity> = {};
|
||||
|
||||
const type: string = this.getStructureType(lgValue);
|
||||
if (ActivityChecker.genericCardTypeMapping.has(type) || type === 'attachment') {
|
||||
activity = MessageFactory.attachment(this.getAttachment(lgValue));
|
||||
} else if (type === 'activity') {
|
||||
activity = this.buildActivity(lgValue);
|
||||
}
|
||||
|
||||
return activity;
|
||||
}
|
||||
|
||||
private static buildActivity(messageValue: any): Partial<Activity> {
|
||||
let activity: Partial<Activity> = { type: ActivityTypes.Message };
|
||||
for (const key of Object.keys(messageValue)) {
|
||||
const property: string = key.trim();
|
||||
if (property === Evaluator.LGType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const value: any = messageValue[key];
|
||||
|
||||
switch (property.toLowerCase()) {
|
||||
case 'attachments':
|
||||
activity.attachments = this.getAttachments(value);
|
||||
break;
|
||||
case 'suggestedactions':
|
||||
activity.suggestedActions = this.getSuggestions(value);
|
||||
break;
|
||||
default:
|
||||
var properties = ActivityChecker.activityProperties.map((u: string): string => u.toLowerCase());
|
||||
if (properties.includes(property.toLowerCase()))
|
||||
{
|
||||
var realPropertyName = ActivityChecker.activityProperties[properties.indexOf(property.toLowerCase())];
|
||||
activity[realPropertyName] = value;
|
||||
} else {
|
||||
activity[property.toLowerCase()] = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return activity;
|
||||
}
|
||||
|
||||
private static getSuggestions(suggestionsValue: any): SuggestedActions {
|
||||
const actions: any[] = this.normalizedToList(suggestionsValue);
|
||||
|
||||
const suggestedActions: SuggestedActions = {
|
||||
actions : this.getCardActions(actions),
|
||||
to: []
|
||||
};
|
||||
|
||||
return suggestedActions;
|
||||
}
|
||||
|
||||
private static getButtons(buttonsValue: any): CardAction[] {
|
||||
const actions: any[] = this.normalizedToList(buttonsValue);
|
||||
return this.getCardActions(actions);
|
||||
}
|
||||
|
||||
private static getCardActions(actions: any[]): CardAction[] {
|
||||
return actions.map((u: any): CardAction => this.getCardAction(u));
|
||||
}
|
||||
|
||||
private static getCardAction(action: any): CardAction
|
||||
{
|
||||
let cardAction: CardAction;
|
||||
if (typeof action === 'string') {
|
||||
cardAction = { type: ActionTypes.ImBack, value: action, title: action, channelData: undefined };
|
||||
} else {
|
||||
const type: string = this.getStructureType(action);
|
||||
cardAction = {
|
||||
type: ActionTypes.ImBack,
|
||||
title: '',
|
||||
value: ''
|
||||
};
|
||||
|
||||
if (type === 'cardaction') {
|
||||
for (const key of Object.keys(action)) {
|
||||
const property: string = key.trim();
|
||||
if (property === Evaluator.LGType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const value: any = action[key];
|
||||
|
||||
switch (property.toLowerCase()) {
|
||||
case 'displaytext':
|
||||
cardAction.displayText = value;
|
||||
break;
|
||||
case 'channeldata':
|
||||
cardAction.channelData = value;
|
||||
break;
|
||||
default:
|
||||
cardAction[property.toLowerCase()] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cardAction;
|
||||
}
|
||||
|
||||
|
||||
private static getStructureType(input: any): string {
|
||||
let result = '';
|
||||
|
||||
if (input && typeof input === 'object') {
|
||||
if (Evaluator.LGType in input) {
|
||||
result = input[Evaluator.LGType].toString();
|
||||
} else if ('type' in input) {
|
||||
// Adaptive card type
|
||||
result = input['type'].toString();
|
||||
}
|
||||
}
|
||||
|
||||
return result.trim().toLowerCase();
|
||||
}
|
||||
|
||||
private static getAttachments(input: any): Attachment[] {
|
||||
const attachments: Attachment[] = [];
|
||||
const attachmentsJsonList: any[] = this.normalizedToList(input);
|
||||
|
||||
for (const attachmentsJson of attachmentsJsonList) {
|
||||
if (typeof attachmentsJson === 'object') {
|
||||
attachments.push(this.getAttachment(attachmentsJson));
|
||||
}
|
||||
}
|
||||
|
||||
return attachments;
|
||||
}
|
||||
|
||||
private static getAttachment(input: any): Attachment {
|
||||
let attachment: Attachment = {
|
||||
contentType: ''
|
||||
};
|
||||
const type: string = this.getStructureType(input);
|
||||
if (ActivityChecker.genericCardTypeMapping.has(type)) {
|
||||
attachment = this.getCardAttachment(ActivityChecker.genericCardTypeMapping.get(type), input);
|
||||
} else if (type === 'adaptivecard') {
|
||||
attachment = CardFactory.adaptiveCard(input);
|
||||
} else if (type === 'attachment') {
|
||||
attachment = this.getNormalAttachment(input);
|
||||
} else {
|
||||
attachment = {contentType: type, content: input};
|
||||
}
|
||||
|
||||
return attachment;
|
||||
}
|
||||
|
||||
private static getNormalAttachment(input: any): Attachment {
|
||||
const attachment: Attachment = {contentType:''};
|
||||
|
||||
for (const key of Object.keys(input)) {
|
||||
const property: string = key.trim();
|
||||
const value: any = input[key];
|
||||
|
||||
switch (property.toLowerCase()) {
|
||||
case 'contenttype':
|
||||
const type: string = value.toString().toLowerCase();
|
||||
if (ActivityChecker.genericCardTypeMapping.has(type)) {
|
||||
attachment.contentType = ActivityChecker.genericCardTypeMapping.get(type);
|
||||
} else if (type === 'adaptivecard') {
|
||||
attachment.contentType = this.adaptiveCardType;
|
||||
} else {
|
||||
attachment.contentType = type;
|
||||
}
|
||||
break;
|
||||
case 'contenturl':
|
||||
attachment.contentUrl = value;
|
||||
break;
|
||||
case 'thumbnailurl':
|
||||
attachment.thumbnailUrl = value;
|
||||
break;
|
||||
default:
|
||||
attachment[property.toLowerCase()] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return attachment;
|
||||
}
|
||||
|
||||
private static getCardAttachment(type: string, input: any): Attachment {
|
||||
const card: any = {};
|
||||
|
||||
for (const key of Object.keys(input)) {
|
||||
const property: string = key.trim().toLowerCase();
|
||||
const value: any = input[key];
|
||||
|
||||
switch (property) {
|
||||
case 'tap':
|
||||
card[property] = this.getCardAction(value);
|
||||
break;
|
||||
case 'image':
|
||||
case 'images':
|
||||
if (type === CardFactory.contentTypes.heroCard || type === CardFactory.contentTypes.thumbnailCard) {
|
||||
if (!('images' in card)) {
|
||||
card['images'] = [];
|
||||
}
|
||||
|
||||
const imageList: string[] = this.normalizedToList(value).map((u): string => u.toString());
|
||||
imageList.forEach( (u): any => card['images'].push({url : u}));
|
||||
} else {
|
||||
card['image'] = {url: value.toString()};
|
||||
}
|
||||
break;
|
||||
case 'media':
|
||||
if (!('media' in card)) {
|
||||
card['media'] = [];
|
||||
}
|
||||
|
||||
const mediaList: string[] = this.normalizedToList(value).map((u): string => u.toString());
|
||||
mediaList.forEach( (u): any => card['media'].push({url : u}));
|
||||
break;
|
||||
case 'buttons':
|
||||
if (!('buttons' in card)) {
|
||||
card['buttons'] = [];
|
||||
}
|
||||
|
||||
const buttons: any[] = this.getButtons(value);
|
||||
buttons.forEach( (u): any => card[property].push(u));
|
||||
break;
|
||||
case 'autostart':
|
||||
case 'shareable':
|
||||
case 'autoloop':
|
||||
const boolValue: boolean = this.getValidBooleanValue(value.toString());
|
||||
if (boolValue !== undefined) {
|
||||
card[property] = boolValue;
|
||||
}
|
||||
break;
|
||||
case 'connectionname':
|
||||
card['connectionName'] = value;
|
||||
break;
|
||||
default:
|
||||
card[property.toLowerCase()] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const attachment: Attachment = {
|
||||
contentType: type,
|
||||
content: card
|
||||
};
|
||||
|
||||
return attachment;
|
||||
}
|
||||
|
||||
private static getValidBooleanValue(boolValue: string): boolean{
|
||||
if (boolValue.toLowerCase() === 'true')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (boolValue.toLowerCase() === 'false')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private static normalizedToList(item: any): any[] {
|
||||
if (item === undefined) {
|
||||
return [];
|
||||
} else if (Array.isArray(item)) {
|
||||
return item;
|
||||
} else {
|
||||
return [item];
|
||||
}
|
||||
}
|
||||
|
||||
private static parseStructuredLGResult(lgStringResult: string): any
|
||||
{
|
||||
let lgStructuredResult: any = undefined;
|
||||
if (lgStringResult === undefined || lgStringResult === '') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
lgStringResult = lgStringResult.trim();
|
||||
|
||||
if (lgStringResult === '' || !lgStringResult.startsWith('{') || !lgStringResult.endsWith('}')) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
lgStructuredResult = JSON.parse(lgStringResult);
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return lgStructuredResult;
|
||||
}
|
||||
}
|
|
@ -12,10 +12,10 @@ import { EvaluationTarget } from './evaluationTarget';
|
|||
import { Evaluator } from './evaluator';
|
||||
import * as lp from './generated/LGFileParser';
|
||||
import { LGFileParserVisitor } from './generated/LGFileParserVisitor';
|
||||
import { LGTemplate } from './lgTemplate';
|
||||
import { LGExtensions } from './lgExtensions';
|
||||
import { Template } from './template';
|
||||
import { TemplateExtensions } from './templateExtensions';
|
||||
import { AnalyzerResult } from './analyzerResult';
|
||||
import {LGErrors} from './lgErrors';
|
||||
import {TemplateErrors} from './templateErrors';
|
||||
|
||||
/**
|
||||
* Analyzer engine. To to get the static analyzer results.
|
||||
|
@ -24,16 +24,16 @@ export class Analyzer extends AbstractParseTreeVisitor<AnalyzerResult> implement
|
|||
/**
|
||||
* Templates.
|
||||
*/
|
||||
public readonly templates: LGTemplate[];
|
||||
public readonly templates: Template[];
|
||||
|
||||
private readonly templateMap: {[name: string]: LGTemplate};
|
||||
private readonly templateMap: {[name: string]: Template};
|
||||
private readonly evalutationTargetStack: EvaluationTarget[] = [];
|
||||
private readonly _expressionParser: ExpressionParserInterface;
|
||||
|
||||
public constructor(templates: LGTemplate[], expressionParser: ExpressionParser) {
|
||||
public constructor(templates: Template[], expressionParser: ExpressionParser) {
|
||||
super();
|
||||
this.templates = templates;
|
||||
this.templateMap = keyBy(templates, (t: LGTemplate): string => t.name);
|
||||
this.templateMap = keyBy(templates, (t: Template): string => t.name);
|
||||
|
||||
// create an evaluator to leverage its customized function look up for checking
|
||||
const evaluator: Evaluator = new Evaluator(this.templates, expressionParser);
|
||||
|
@ -47,11 +47,11 @@ export class Analyzer extends AbstractParseTreeVisitor<AnalyzerResult> implement
|
|||
*/
|
||||
public analyzeTemplate(templateName: string): AnalyzerResult {
|
||||
if (!(templateName in this.templateMap)) {
|
||||
throw new Error(LGErrors.templateNotExist(templateName));
|
||||
throw new Error(TemplateErrors.templateNotExist(templateName));
|
||||
}
|
||||
|
||||
if (this.evalutationTargetStack.find((u: EvaluationTarget): boolean => u.templateName === templateName) !== undefined) {
|
||||
throw new Error(`${ LGErrors.loopDetected } ${ this.evalutationTargetStack.reverse()
|
||||
throw new Error(`${ TemplateErrors.loopDetected } ${ this.evalutationTargetStack.reverse()
|
||||
.map((u: EvaluationTarget): string => u.templateName)
|
||||
.join(' => ') }`);
|
||||
}
|
||||
|
@ -114,8 +114,8 @@ export class Analyzer extends AbstractParseTreeVisitor<AnalyzerResult> implement
|
|||
|
||||
const values = ctx.keyValueStructureValue();
|
||||
for (const value of values) {
|
||||
if (LGExtensions.isPureExpression(value).hasExpr) {
|
||||
result.union(this.analyzeExpression(LGExtensions.isPureExpression(value).expression));
|
||||
if (TemplateExtensions.isPureExpression(value).hasExpr) {
|
||||
result.union(this.analyzeExpression(TemplateExtensions.isPureExpression(value).expression));
|
||||
} else {
|
||||
const exprs = value.EXPRESSION_IN_STRUCTURE_BODY();
|
||||
for (const expr of exprs) {
|
||||
|
@ -198,7 +198,7 @@ export class Analyzer extends AbstractParseTreeVisitor<AnalyzerResult> implement
|
|||
|
||||
private analyzeExpression(exp: string): AnalyzerResult {
|
||||
const result: AnalyzerResult = new AnalyzerResult();
|
||||
exp = LGExtensions.trimExpression(exp);
|
||||
exp = TemplateExtensions.trimExpression(exp);
|
||||
const parsed: Expression = this._expressionParser.parse(exp);
|
||||
|
||||
const references: readonly string[] = Extensions.references(parsed);
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
*/
|
||||
import { ANTLRErrorListener, RecognitionException, Recognizer } from 'antlr4ts';
|
||||
import { Diagnostic, DiagnosticSeverity } from './diagnostic';
|
||||
import { LGException } from './lgException';
|
||||
import { TemplateException } from './templateException';
|
||||
import { Position } from './position';
|
||||
import { Range } from './range';
|
||||
import { LGErrors } from './lgErrors';
|
||||
import { TemplateErrors } from './templateErrors';
|
||||
|
||||
/**
|
||||
* LG parser error listener.
|
||||
|
@ -33,8 +33,8 @@ export class ErrorListener implements ANTLRErrorListener<any> {
|
|||
const startPosition: Position = new Position(line, charPositionInLine);
|
||||
const stopPosition: Position = new Position(line, charPositionInLine + offendingSymbol.stopIndex - offendingSymbol.startIndex + 1);
|
||||
const range: Range = new Range(startPosition, stopPosition);
|
||||
const diagnostic: Diagnostic = new Diagnostic(range, LGErrors.syntaxError, DiagnosticSeverity.Error, this.source);
|
||||
const diagnostic: Diagnostic = new Diagnostic(range, TemplateErrors.syntaxError, DiagnosticSeverity.Error, this.source);
|
||||
|
||||
throw new LGException(diagnostic.toString(), [diagnostic]);
|
||||
throw new TemplateException(diagnostic.toString(), [diagnostic]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,11 +13,11 @@ import { CustomizedMemory } from './customizedMemory';
|
|||
import { EvaluationTarget } from './evaluationTarget';
|
||||
import * as lp from './generated/LGFileParser';
|
||||
import { LGFileParserVisitor } from './generated/LGFileParserVisitor';
|
||||
import { LGTemplate } from './lgTemplate';
|
||||
import { Template } from './template';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { LGExtensions } from './lgExtensions';
|
||||
import { LGErrors } from './lgErrors';
|
||||
import { TemplateExtensions } from './templateExtensions';
|
||||
import { TemplateErrors } from './templateErrors';
|
||||
/**
|
||||
* Evaluation runtime engine
|
||||
*/
|
||||
|
@ -26,7 +26,7 @@ export class Evaluator extends AbstractParseTreeVisitor<any> implements LGFilePa
|
|||
/**
|
||||
* Templates.
|
||||
*/
|
||||
public readonly templates: LGTemplate[];
|
||||
public readonly templates: Template[];
|
||||
|
||||
/**
|
||||
* Expression parser.
|
||||
|
@ -36,7 +36,7 @@ export class Evaluator extends AbstractParseTreeVisitor<any> implements LGFilePa
|
|||
/**
|
||||
* TemplateMap.
|
||||
*/
|
||||
public readonly templateMap: { [name: string]: LGTemplate };
|
||||
public readonly templateMap: { [name: string]: Template };
|
||||
private readonly evaluationTargetStack: EvaluationTarget[] = [];
|
||||
private readonly strictMode: boolean;
|
||||
|
||||
|
@ -49,12 +49,12 @@ export class Evaluator extends AbstractParseTreeVisitor<any> implements LGFilePa
|
|||
public static readonly fromFileFunctionName = 'fromFile';
|
||||
public static readonly templateFunctionName = 'template';
|
||||
public static readonly isTemplateFunctionName = 'isTemplate';
|
||||
private static readonly ReExecuteSuffix = '!';
|
||||
public static readonly ReExecuteSuffix = '!';
|
||||
|
||||
public constructor(templates: LGTemplate[], expressionParser: ExpressionParser, strictMode: boolean = false) {
|
||||
public constructor(templates: Template[], expressionParser: ExpressionParser, strictMode: boolean = false) {
|
||||
super();
|
||||
this.templates = templates;
|
||||
this.templateMap = keyBy(templates, (t: LGTemplate): string => t.name);
|
||||
this.templateMap = keyBy(templates, (t: Template): string => t.name);
|
||||
this.strictMode = strictMode;
|
||||
|
||||
// generate a new customzied expression parser by injecting the templates as functions
|
||||
|
@ -77,11 +77,11 @@ export class Evaluator extends AbstractParseTreeVisitor<any> implements LGFilePa
|
|||
({reExecute, pureTemplateName: templateName} = this.parseTemplateName(inputTemplateName));
|
||||
|
||||
if (!(templateName in this.templateMap)) {
|
||||
throw new Error(LGErrors.templateNotExist(templateName));
|
||||
throw new Error(TemplateErrors.templateNotExist(templateName));
|
||||
}
|
||||
|
||||
if (this.evaluationTargetStack.some((u: EvaluationTarget): boolean => u.templateName === templateName)) {
|
||||
throw new Error(`${ LGErrors.loopDetected } ${ this.evaluationTargetStack.reverse()
|
||||
throw new Error(`${ TemplateErrors.loopDetected } ${ this.evaluationTargetStack.reverse()
|
||||
.map((u: EvaluationTarget): string => u.templateName)
|
||||
.join(' => ') }`);
|
||||
}
|
||||
|
@ -148,14 +148,14 @@ export class Evaluator extends AbstractParseTreeVisitor<any> implements LGFilePa
|
|||
|
||||
const result = [];
|
||||
for(const item of values) {
|
||||
if (LGExtensions.isPureExpression(item).hasExpr) {
|
||||
result.push(this.evalExpression(LGExtensions.isPureExpression(item).expression, ctx));
|
||||
if (TemplateExtensions.isPureExpression(item).hasExpr) {
|
||||
result.push(this.evalExpression(TemplateExtensions.isPureExpression(item).expression, ctx));
|
||||
} else {
|
||||
let itemStringResult = '';
|
||||
for(const node of item.children) {
|
||||
switch ((node as TerminalNode).symbol.type) {
|
||||
case (lp.LGFileParser.ESCAPE_CHARACTER_IN_STRUCTURE_BODY):
|
||||
itemStringResult += LGExtensions.evalEscape(node.text);
|
||||
itemStringResult += TemplateExtensions.evalEscape(node.text);
|
||||
break;
|
||||
|
||||
case (lp.LGFileParser.EXPRESSION_IN_STRUCTURE_BODY):
|
||||
|
@ -208,7 +208,7 @@ export class Evaluator extends AbstractParseTreeVisitor<any> implements LGFilePa
|
|||
}
|
||||
|
||||
public visitNormalTemplateString(ctx: lp.NormalTemplateStringContext): string {
|
||||
const prefixErrorMsg = LGExtensions.getPrefixErrorMessage(ctx);
|
||||
const prefixErrorMsg = TemplateExtensions.getPrefixErrorMessage(ctx);
|
||||
const result: any[] = [];
|
||||
for (const node of ctx.children) {
|
||||
const innerNode: TerminalNode = node as TerminalNode;
|
||||
|
@ -218,7 +218,7 @@ export class Evaluator extends AbstractParseTreeVisitor<any> implements LGFilePa
|
|||
case lp.LGFileParser.DASH:
|
||||
break;
|
||||
case lp.LGFileParser.ESCAPE_CHARACTER:
|
||||
result.push(LGExtensions.evalEscape(innerNode.text));
|
||||
result.push(TemplateExtensions.evalEscape(innerNode.text));
|
||||
break;
|
||||
case lp.LGFileParser.EXPRESSION:
|
||||
result.push(this.evalExpression(innerNode.text, ctx, prefixErrorMsg));
|
||||
|
@ -247,7 +247,7 @@ export class Evaluator extends AbstractParseTreeVisitor<any> implements LGFilePa
|
|||
var templateName = this.parseTemplateName(inputTemplateName).pureTemplateName;
|
||||
|
||||
if (!(templateName in this.templateMap)) {
|
||||
throw new Error(LGErrors.templateNotExist(templateName));
|
||||
throw new Error(TemplateErrors.templateNotExist(templateName));
|
||||
}
|
||||
|
||||
const parameters: string[] = this.templateMap[templateName].parameters;
|
||||
|
@ -262,7 +262,7 @@ export class Evaluator extends AbstractParseTreeVisitor<any> implements LGFilePa
|
|||
parameters.map((e: string, i: number): void => newScope[e] = args[i]);
|
||||
const memory = currentScope as CustomizedMemory;
|
||||
if (!memory) {
|
||||
throw new Error(LGErrors.invalidMemory);
|
||||
throw new Error(TemplateErrors.invalidMemory);
|
||||
}
|
||||
|
||||
return new CustomizedMemory(memory.globalMemory, SimpleObjectMemory.wrap(newScope));
|
||||
|
@ -333,7 +333,7 @@ export class Evaluator extends AbstractParseTreeVisitor<any> implements LGFilePa
|
|||
}
|
||||
|
||||
private evalExpressionInCondition(exp: string, context?: ParserRuleContext, errorPrefix: string = ''): boolean {
|
||||
exp = LGExtensions.trimExpression(exp);
|
||||
exp = TemplateExtensions.trimExpression(exp);
|
||||
let result: any;
|
||||
let error: string;
|
||||
({value: result, error: error} = this.evalByAdaptiveExpression(exp, this.currentTarget().scope));
|
||||
|
@ -349,12 +349,12 @@ export class Evaluator extends AbstractParseTreeVisitor<any> implements LGFilePa
|
|||
}
|
||||
else if (!result)
|
||||
{
|
||||
childErrorMsg += LGErrors.nullExpression(exp);
|
||||
childErrorMsg += TemplateErrors.nullExpression(exp);
|
||||
}
|
||||
|
||||
if (context)
|
||||
{
|
||||
errorMsg += LGErrors.errorExpression(context.text, this.currentTarget().templateName, errorPrefix);
|
||||
errorMsg += TemplateErrors.errorExpression(context.text, this.currentTarget().templateName, errorPrefix);
|
||||
}
|
||||
|
||||
if (this.evaluationTargetStack.length > 0)
|
||||
|
@ -373,7 +373,7 @@ export class Evaluator extends AbstractParseTreeVisitor<any> implements LGFilePa
|
|||
|
||||
private evalExpression(exp: string, context?: ParserRuleContext, errorPrefix: string = ''): any
|
||||
{
|
||||
exp = LGExtensions.trimExpression(exp);
|
||||
exp = TemplateExtensions.trimExpression(exp);
|
||||
let result: any;
|
||||
let error: string;
|
||||
({value: result, error: error} = this.evalByAdaptiveExpression(exp, this.currentTarget().scope));
|
||||
|
@ -389,12 +389,12 @@ export class Evaluator extends AbstractParseTreeVisitor<any> implements LGFilePa
|
|||
}
|
||||
else if (result === undefined)
|
||||
{
|
||||
childErrorMsg += LGErrors.nullExpression(exp);
|
||||
childErrorMsg += TemplateErrors.nullExpression(exp);
|
||||
}
|
||||
|
||||
if (context)
|
||||
{
|
||||
errorMsg += LGErrors.errorExpression(context.text, this.currentTarget().templateName, errorPrefix);
|
||||
errorMsg += TemplateErrors.errorExpression(context.text, this.currentTarget().templateName, errorPrefix);
|
||||
}
|
||||
|
||||
if (this.evaluationTargetStack.length > 0)
|
||||
|
@ -464,12 +464,12 @@ export class Evaluator extends AbstractParseTreeVisitor<any> implements LGFilePa
|
|||
}
|
||||
|
||||
private readonly fromFile = (): any => (args: readonly any[]): any => {
|
||||
const filePath: string = LGExtensions.normalizePath(args[0].toString());
|
||||
const filePath: string = TemplateExtensions.normalizePath(args[0].toString());
|
||||
const resourcePath: string = this.getResourcePath(filePath);
|
||||
const stringContent = fs.readFileSync(resourcePath, 'utf-8');
|
||||
|
||||
const result = this.wrappedEvalTextContainsExpression(stringContent, Evaluator.expressionRecognizeReverseRegex);
|
||||
return LGExtensions.evalEscape(result);
|
||||
return TemplateExtensions.evalEscape(result);
|
||||
}
|
||||
|
||||
private getResourcePath(filePath: string): string {
|
||||
|
@ -482,8 +482,8 @@ export class Evaluator extends AbstractParseTreeVisitor<any> implements LGFilePa
|
|||
if (inBrowser) {
|
||||
throw new Error('relative path is not support in browser.');
|
||||
}
|
||||
const template: LGTemplate = this.templateMap[this.currentTarget().templateName];
|
||||
const sourcePath: string = LGExtensions.normalizePath(template.source);
|
||||
const template: Template = this.templateMap[this.currentTarget().templateName];
|
||||
const sourcePath: string = TemplateExtensions.normalizePath(template.source);
|
||||
let baseFolder: string = __dirname;
|
||||
if (path.isAbsolute(sourcePath)) {
|
||||
baseFolder = path.dirname(sourcePath);
|
||||
|
@ -518,7 +518,7 @@ export class Evaluator extends AbstractParseTreeVisitor<any> implements LGFilePa
|
|||
|
||||
// Validate return type
|
||||
if (children0.returnType !== ReturnType.Object && children0.returnType !== ReturnType.String) {
|
||||
throw new Error(LGErrors.errorTemplateNameformat(children0.toString()));
|
||||
throw new Error(TemplateErrors.errorTemplateNameformat(children0.toString()));
|
||||
}
|
||||
|
||||
// Validate more if the name is string constant
|
||||
|
@ -531,7 +531,7 @@ export class Evaluator extends AbstractParseTreeVisitor<any> implements LGFilePa
|
|||
private checkTemplateReference(templateName: string, children: Expression[]): void{
|
||||
if (!(templateName in this.templateMap))
|
||||
{
|
||||
throw new Error(LGErrors.templateNotExist(templateName));
|
||||
throw new Error(TemplateErrors.templateNotExist(templateName));
|
||||
}
|
||||
|
||||
var expectedArgsCount = this.templateMap[templateName].parameters.length;
|
||||
|
@ -539,7 +539,7 @@ export class Evaluator extends AbstractParseTreeVisitor<any> implements LGFilePa
|
|||
|
||||
if (actualArgsCount !== 0 && expectedArgsCount !== actualArgsCount)
|
||||
{
|
||||
throw new Error(LGErrors.argumentMismatch(templateName, expectedArgsCount, actualArgsCount));
|
||||
throw new Error(TemplateErrors.argumentMismatch(templateName, expectedArgsCount, actualArgsCount));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -550,18 +550,7 @@ export class Evaluator extends AbstractParseTreeVisitor<any> implements LGFilePa
|
|||
}
|
||||
|
||||
private readonly validTemplateReference = (expression: Expression): void => {
|
||||
const templateName: string = expression.type;
|
||||
|
||||
if (!(templateName in this.templateMap)) {
|
||||
throw new Error(`no such template '${ templateName }' to call in ${ expression }`);
|
||||
}
|
||||
|
||||
const expectedArgsCount: number = this.templateMap[templateName].parameters.length;
|
||||
const actualArgsCount: number = expression.children.length;
|
||||
|
||||
if (expectedArgsCount !== actualArgsCount) {
|
||||
throw new Error(LGErrors.argumentMismatch(templateName, expectedArgsCount, actualArgsCount));
|
||||
}
|
||||
return this.checkTemplateReference(expression.type, expression.children);
|
||||
}
|
||||
|
||||
private parseTemplateName(templateName: string): { reExecute: boolean; pureTemplateName: string } {
|
||||
|
|
|
@ -7,16 +7,18 @@
|
|||
*/
|
||||
import { AbstractParseTreeVisitor, TerminalNode } from 'antlr4ts/tree';
|
||||
import { ParserRuleContext } from 'antlr4ts/ParserRuleContext';
|
||||
import { ExpressionFunctions, EvaluatorLookup, Expression, ExpressionParser, ExpressionEvaluator, ReturnType, SimpleObjectMemory } from 'adaptive-expressions';
|
||||
import { ExpressionFunctions, EvaluatorLookup, Expression, ExpressionParser, ExpressionEvaluator, ReturnType, SimpleObjectMemory, ExpressionType, Constant } from 'adaptive-expressions';
|
||||
import { keyBy } from 'lodash';
|
||||
import { EvaluationTarget } from './evaluationTarget';
|
||||
import { Evaluator } from './evaluator';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as lp from './generated/LGFileParser';
|
||||
import { LGFileParserVisitor } from './generated/LGFileParserVisitor';
|
||||
import { LGTemplate } from './lgTemplate';
|
||||
import { LGExtensions } from './lgExtensions';
|
||||
import { Template } from './template';
|
||||
import { TemplateExtensions } from './templateExtensions';
|
||||
import { CustomizedMemory } from './customizedMemory';
|
||||
import { LGErrors } from './lgErrors';
|
||||
import { TemplateErrors } from './templateErrors';
|
||||
|
||||
/**
|
||||
* LG template expander.
|
||||
|
@ -25,21 +27,21 @@ export class Expander extends AbstractParseTreeVisitor<string[]> implements LGFi
|
|||
/**
|
||||
* Templates.
|
||||
*/
|
||||
public readonly templates: LGTemplate[];
|
||||
public readonly templates: Template[];
|
||||
|
||||
/**
|
||||
* TemplateMap.
|
||||
*/
|
||||
public readonly templateMap: {[name: string]: LGTemplate};
|
||||
public readonly templateMap: {[name: string]: Template};
|
||||
private readonly evaluationTargetStack: EvaluationTarget[] = [];
|
||||
private readonly expanderExpressionParser: ExpressionParser;
|
||||
private readonly evaluatorExpressionParser: ExpressionParser;
|
||||
private readonly strictMode: boolean;
|
||||
|
||||
public constructor(templates: LGTemplate[], expressionParser: ExpressionParser, strictMode: boolean = false) {
|
||||
public constructor(templates: Template[], expressionParser: ExpressionParser, strictMode: boolean = false) {
|
||||
super();
|
||||
this.templates = templates;
|
||||
this.templateMap = keyBy(templates, (t: LGTemplate): string => t.name);
|
||||
this.templateMap = keyBy(templates, (t: Template): string => t.name);
|
||||
this.strictMode = strictMode;
|
||||
|
||||
// generate a new customzied expression parser by injecting the template as functions
|
||||
|
@ -55,11 +57,11 @@ export class Expander extends AbstractParseTreeVisitor<string[]> implements LGFi
|
|||
*/
|
||||
public expandTemplate(templateName: string, scope: any): string[] {
|
||||
if (!(templateName in this.templateMap)) {
|
||||
throw new Error(LGErrors.templateNotExist(templateName));
|
||||
throw new Error(TemplateErrors.templateNotExist(templateName));
|
||||
}
|
||||
|
||||
if (this.evaluationTargetStack.find((u: EvaluationTarget): boolean => u.templateName === templateName)) {
|
||||
throw new Error(`${ LGErrors.loopDetected } ${ this.evaluationTargetStack.reverse()
|
||||
throw new Error(`${ TemplateErrors.loopDetected } ${ this.evaluationTargetStack.reverse()
|
||||
.map((u: EvaluationTarget): string => u.templateName)
|
||||
.join(' => ') }`);
|
||||
}
|
||||
|
@ -126,14 +128,14 @@ export class Expander extends AbstractParseTreeVisitor<string[]> implements LGFi
|
|||
if (value.length > 1) {
|
||||
const valueList = [];
|
||||
for (const item of value) {
|
||||
const id = LGExtensions.newGuid();
|
||||
const id = TemplateExtensions.newGuid();
|
||||
valueList.push(id);
|
||||
templateRefValues.set(id, item);
|
||||
}
|
||||
|
||||
expandedResult.forEach((x): any[] => x[property] = valueList);
|
||||
} else {
|
||||
const id = LGExtensions.newGuid();
|
||||
const id = TemplateExtensions.newGuid();
|
||||
expandedResult.forEach((x): string => x[property] = id);
|
||||
templateRefValues.set(id, value[0]);
|
||||
}
|
||||
|
@ -184,14 +186,14 @@ export class Expander extends AbstractParseTreeVisitor<string[]> implements LGFi
|
|||
|
||||
let result: any[] = [];
|
||||
for (const item of values) {
|
||||
if (LGExtensions.isPureExpression(item).hasExpr) {
|
||||
result.push(this.evalExpression(LGExtensions.isPureExpression(item).expression, ctx));
|
||||
if (TemplateExtensions.isPureExpression(item).hasExpr) {
|
||||
result.push(this.evalExpression(TemplateExtensions.isPureExpression(item).expression, ctx));
|
||||
} else {
|
||||
let itemStringResult = [''];
|
||||
for (const node of item.children) {
|
||||
switch ((node as TerminalNode).symbol.type) {
|
||||
case (lp.LGFileParser.ESCAPE_CHARACTER_IN_STRUCTURE_BODY):
|
||||
itemStringResult = this.stringArrayConcat(itemStringResult, [LGExtensions.evalEscape(node.text)]);
|
||||
itemStringResult = this.stringArrayConcat(itemStringResult, [TemplateExtensions.evalEscape(node.text)]);
|
||||
break;
|
||||
case (lp.LGFileParser.EXPRESSION_IN_STRUCTURE_BODY):
|
||||
const errorPrefix = `Property '${ ctx.STRUCTURE_IDENTIFIER().text }':`;
|
||||
|
@ -248,7 +250,7 @@ export class Expander extends AbstractParseTreeVisitor<string[]> implements LGFi
|
|||
}
|
||||
|
||||
public visitNormalTemplateString(ctx: lp.NormalTemplateStringContext): string[] {
|
||||
var prefixErrorMsg = LGExtensions.getPrefixErrorMessage(ctx);
|
||||
var prefixErrorMsg = TemplateExtensions.getPrefixErrorMessage(ctx);
|
||||
let result: string[] = [''];
|
||||
for (const node of ctx.children) {
|
||||
const innerNode: TerminalNode = node as TerminalNode;
|
||||
|
@ -258,7 +260,7 @@ export class Expander extends AbstractParseTreeVisitor<string[]> implements LGFi
|
|||
case lp.LGFileParser.DASH:
|
||||
break;
|
||||
case lp.LGFileParser.ESCAPE_CHARACTER:
|
||||
result = this.stringArrayConcat(result, [LGExtensions.evalEscape(innerNode.text)]);
|
||||
result = this.stringArrayConcat(result, [TemplateExtensions.evalEscape(innerNode.text)]);
|
||||
break;
|
||||
case lp.LGFileParser.EXPRESSION: {
|
||||
result = this.stringArrayConcat(result, this.evalExpression(innerNode.text, ctx, prefixErrorMsg));
|
||||
|
@ -310,7 +312,7 @@ export class Expander extends AbstractParseTreeVisitor<string[]> implements LGFi
|
|||
}
|
||||
|
||||
private evalExpressionInCondition(exp: string, context?: ParserRuleContext, errorPrefix: string = ''): boolean {
|
||||
exp = LGExtensions.trimExpression(exp);
|
||||
exp = TemplateExtensions.trimExpression(exp);
|
||||
let result: any;
|
||||
let error: string;
|
||||
({value: result, error: error} = this.evalByAdaptiveExpression(exp, this.currentTarget().scope));
|
||||
|
@ -326,12 +328,12 @@ export class Expander extends AbstractParseTreeVisitor<string[]> implements LGFi
|
|||
}
|
||||
else if (!result)
|
||||
{
|
||||
childErrorMsg += LGErrors.nullExpression(exp);
|
||||
childErrorMsg += TemplateErrors.nullExpression(exp);
|
||||
}
|
||||
|
||||
if (context)
|
||||
{
|
||||
errorMsg += LGErrors.errorExpression(context.text, this.currentTarget().templateName, errorPrefix);
|
||||
errorMsg += TemplateErrors.errorExpression(context.text, this.currentTarget().templateName, errorPrefix);
|
||||
}
|
||||
|
||||
if (this.evaluationTargetStack.length > 0)
|
||||
|
@ -349,7 +351,7 @@ export class Expander extends AbstractParseTreeVisitor<string[]> implements LGFi
|
|||
}
|
||||
|
||||
private evalExpression(exp: string, context: ParserRuleContext, errorPrefix: string = ''): string[] {
|
||||
exp = LGExtensions.trimExpression(exp);
|
||||
exp = TemplateExtensions.trimExpression(exp);
|
||||
let result: any;
|
||||
let error: string;
|
||||
({value: result, error: error} = this.evalByAdaptiveExpression(exp, this.currentTarget().scope));
|
||||
|
@ -365,12 +367,12 @@ export class Expander extends AbstractParseTreeVisitor<string[]> implements LGFi
|
|||
}
|
||||
else if (result === undefined)
|
||||
{
|
||||
childErrorMsg += LGErrors.nullExpression(exp);
|
||||
childErrorMsg += TemplateErrors.nullExpression(exp);
|
||||
}
|
||||
|
||||
if (context !== undefined)
|
||||
{
|
||||
errorMsg += LGErrors.errorExpression(context.text, this.currentTarget().templateName, errorPrefix);
|
||||
errorMsg += TemplateErrors.errorExpression(context.text, this.currentTarget().templateName, errorPrefix);
|
||||
}
|
||||
|
||||
if (this.evaluationTargetStack.length > 0)
|
||||
|
@ -412,21 +414,46 @@ export class Expander extends AbstractParseTreeVisitor<string[]> implements LGFi
|
|||
}
|
||||
|
||||
private readonly customizedEvaluatorLookup = (baseLookup: EvaluatorLookup, isExpander: boolean): any => (name: string): ExpressionEvaluator => {
|
||||
const prebuiltPrefix = 'prebuilt.';
|
||||
const standardFunction = baseLookup(name);
|
||||
|
||||
if (name.startsWith(prebuiltPrefix)) {
|
||||
return baseLookup(name.substring(prebuiltPrefix.length));
|
||||
if (standardFunction !== undefined) {
|
||||
return standardFunction;
|
||||
}
|
||||
|
||||
if (name.startsWith('lg.')) {
|
||||
name = name.substring(3);
|
||||
}
|
||||
|
||||
if (this.templateMap[name]) {
|
||||
var templateName = this.parseTemplateName(name).pureTemplateName;
|
||||
if (templateName in this.templateMap) {
|
||||
if (isExpander) {
|
||||
return new ExpressionEvaluator(name, ExpressionFunctions.apply(this.templateExpander(name)), ReturnType.String, this.validTemplateReference);
|
||||
return new ExpressionEvaluator(templateName, ExpressionFunctions.apply(this.templateExpander(name)), ReturnType.Object, this.validTemplateReference);
|
||||
} else {
|
||||
return new ExpressionEvaluator(name, ExpressionFunctions.apply(this.templateEvaluator(name)), ReturnType.String, this.validTemplateReference);
|
||||
return new ExpressionEvaluator(templateName, ExpressionFunctions.apply(this.templateEvaluator(name)), ReturnType.Object, this.validTemplateReference);
|
||||
}
|
||||
}
|
||||
|
||||
return baseLookup(name);
|
||||
if (name === Evaluator.templateFunctionName) {
|
||||
return new ExpressionEvaluator(Evaluator.templateFunctionName, ExpressionFunctions.apply(this.templateFunction()), ReturnType.Object, this.validateTemplateFunction);
|
||||
}
|
||||
|
||||
if (name === Evaluator.fromFileFunctionName) {
|
||||
return new ExpressionEvaluator(Evaluator.fromFileFunctionName, ExpressionFunctions.apply(this.fromFile()), ReturnType.Object, ExpressionFunctions.validateUnaryString);
|
||||
}
|
||||
|
||||
if (name === Evaluator.activityAttachmentFunctionName) {
|
||||
return new ExpressionEvaluator(
|
||||
Evaluator.activityAttachmentFunctionName,
|
||||
ExpressionFunctions.apply(this.activityAttachment()),
|
||||
ReturnType.Object,
|
||||
(expr): void => ExpressionFunctions.validateOrder(expr, undefined, ReturnType.Object, ReturnType.String));
|
||||
}
|
||||
|
||||
if (name === Evaluator.isTemplateFunctionName) {
|
||||
return new ExpressionEvaluator(Evaluator.isTemplateFunctionName, ExpressionFunctions.apply(this.isTemplate()), ReturnType.Boolean, ExpressionFunctions.validateUnaryString);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private readonly templateEvaluator = (templateName: string): any => (args: readonly any[]): string => {
|
||||
|
@ -444,21 +471,6 @@ export class Expander extends AbstractParseTreeVisitor<string[]> implements LGFi
|
|||
return this.expandTemplate(templateName, newScope);
|
||||
}
|
||||
|
||||
private readonly validTemplateReference = (expression: Expression): void => {
|
||||
const templateName: string = expression.type;
|
||||
|
||||
if (!this.templateMap[templateName]) {
|
||||
throw new Error(LGErrors.templateNotExist(templateName));
|
||||
}
|
||||
|
||||
const expectedArgsCount: number = this.templateMap[templateName].parameters.length;
|
||||
const actualArgsCount: number = expression.children.length;
|
||||
|
||||
if (expectedArgsCount !== actualArgsCount) {
|
||||
throw new Error(LGErrors.argumentMismatch(templateName, expectedArgsCount, actualArgsCount));
|
||||
}
|
||||
}
|
||||
|
||||
private reconstructExpression(expanderExpression: Expression, evaluatorExpression: Expression, foundPrebuiltFunction: boolean): Expression {
|
||||
if (this.templateMap[expanderExpression.type]) {
|
||||
if (foundPrebuiltFunction) {
|
||||
|
@ -474,4 +486,110 @@ export class Expander extends AbstractParseTreeVisitor<string[]> implements LGFi
|
|||
|
||||
return expanderExpression;
|
||||
}
|
||||
|
||||
private readonly isTemplate = (): any => (args: readonly any[]): boolean => {
|
||||
const templateName = args[0].toString();
|
||||
return templateName in this.templateMap;
|
||||
}
|
||||
|
||||
private readonly fromFile = (): any => (args: readonly any[]): any => {
|
||||
const filePath: string = TemplateExtensions.normalizePath(args[0].toString());
|
||||
const resourcePath: string = this.getResourcePath(filePath);
|
||||
const stringContent = fs.readFileSync(resourcePath, 'utf-8');
|
||||
|
||||
// TODO
|
||||
return stringContent;
|
||||
//const result = this.wrappedEvalTextContainsExpression(stringContent, Evaluator.expressionRecognizeReverseRegex);
|
||||
//return TemplateExtensions.evalEscape(result);
|
||||
}
|
||||
|
||||
private getResourcePath(filePath: string): string {
|
||||
let resourcePath: string;
|
||||
if (path.isAbsolute(filePath)) {
|
||||
resourcePath = filePath;
|
||||
} else {
|
||||
// relative path is not support in broswer environment
|
||||
const inBrowser: boolean = typeof window !== 'undefined';
|
||||
if (inBrowser) {
|
||||
throw new Error('relative path is not support in browser.');
|
||||
}
|
||||
const template: Template = this.templateMap[this.currentTarget().templateName];
|
||||
const sourcePath: string = TemplateExtensions.normalizePath(template.source);
|
||||
let baseFolder: string = __dirname;
|
||||
if (path.isAbsolute(sourcePath)) {
|
||||
baseFolder = path.dirname(sourcePath);
|
||||
}
|
||||
|
||||
resourcePath = path.join(baseFolder, filePath);
|
||||
}
|
||||
|
||||
return resourcePath;
|
||||
}
|
||||
|
||||
private readonly activityAttachment = (): any => (args: readonly any[]): any => {
|
||||
return {
|
||||
[Evaluator.LGType]: 'attachment',
|
||||
contenttype: args[1].toString(),
|
||||
content: args[0]
|
||||
};
|
||||
}
|
||||
|
||||
private readonly templateFunction = (): any => (args: readonly any[]): any => {
|
||||
const templateName: string = args[0];
|
||||
const newScope: any = this.constructScope(templateName, args.slice(1));
|
||||
|
||||
const value: string[] = this.expandTemplate(templateName, newScope);
|
||||
const randomNumber: number = Math.floor(Math.random() * value.length);
|
||||
|
||||
return value[randomNumber];
|
||||
}
|
||||
|
||||
private readonly validateTemplateFunction = (expression: Expression): void => {
|
||||
|
||||
ExpressionFunctions.validateAtLeastOne(expression);
|
||||
|
||||
const children0: Expression = expression.children[0];
|
||||
|
||||
// Validate return type
|
||||
if (children0.returnType !== ReturnType.Object && children0.returnType !== ReturnType.String) {
|
||||
throw new Error(TemplateErrors.errorTemplateNameformat(children0.toString()));
|
||||
}
|
||||
|
||||
// Validate more if the name is string constant
|
||||
if (children0.type === ExpressionType.Constant) {
|
||||
const templateName: string = (children0 as Constant).value;
|
||||
this.checkTemplateReference(templateName, expression.children.slice(1));
|
||||
}
|
||||
}
|
||||
|
||||
private checkTemplateReference(templateName: string, children: Expression[]): void{
|
||||
if (!(templateName in this.templateMap))
|
||||
{
|
||||
throw new Error(TemplateErrors.templateNotExist(templateName));
|
||||
}
|
||||
|
||||
var expectedArgsCount = this.templateMap[templateName].parameters.length;
|
||||
var actualArgsCount = children.length;
|
||||
|
||||
if (actualArgsCount !== 0 && expectedArgsCount !== actualArgsCount)
|
||||
{
|
||||
throw new Error(TemplateErrors.argumentMismatch(templateName, expectedArgsCount, actualArgsCount));
|
||||
}
|
||||
}
|
||||
|
||||
private readonly validTemplateReference = (expression: Expression): void => {
|
||||
return this.checkTemplateReference(expression.type, expression.children);
|
||||
}
|
||||
|
||||
private parseTemplateName(templateName: string): { reExecute: boolean; pureTemplateName: string } {
|
||||
if (!templateName) {
|
||||
throw new Error('template name is empty.');
|
||||
}
|
||||
|
||||
if (templateName.endsWith(Evaluator.ReExecuteSuffix)) {
|
||||
return {reExecute:true, pureTemplateName: templateName.substr(0, templateName.length - Evaluator.ReExecuteSuffix.length)};
|
||||
} else {
|
||||
return {reExecute:false, pureTemplateName: templateName};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,23 +10,23 @@ import { AbstractParseTreeVisitor, TerminalNode } from 'antlr4ts/tree';
|
|||
import { keyBy } from 'lodash';
|
||||
import * as lp from './generated/LGFileParser';
|
||||
import { LGFileParserVisitor } from './generated/LGFileParserVisitor';
|
||||
import { LGTemplate } from './lgTemplate';
|
||||
import { Template } from './template';
|
||||
|
||||
/**
|
||||
* Lg template extracter.
|
||||
*/
|
||||
export class Extractor extends AbstractParseTreeVisitor<Map<string, any>> implements LGFileParserVisitor<Map<string, any>> {
|
||||
public readonly templates: LGTemplate[];
|
||||
public readonly templateMap: {[name: string]: LGTemplate};
|
||||
public constructor(templates: LGTemplate[]) {
|
||||
public readonly templates: Template[];
|
||||
public readonly templateMap: {[name: string]: Template};
|
||||
public constructor(templates: Template[]) {
|
||||
super();
|
||||
this.templates = templates;
|
||||
this.templateMap = keyBy(templates, (t: LGTemplate): string => t.name);
|
||||
this.templateMap = keyBy(templates, (t: Template): string => t.name);
|
||||
}
|
||||
|
||||
public extract(): Map<string, any>[] {
|
||||
const result: Map<string, any>[] = [];
|
||||
this.templates.forEach((template: LGTemplate): any => {
|
||||
this.templates.forEach((template: Template): any => {
|
||||
result.push(this.visit(template.parseTree));
|
||||
});
|
||||
|
||||
|
|
|
@ -5,26 +5,25 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
export * from './lgFile';
|
||||
export * from './templates';
|
||||
export * from './evaluator';
|
||||
export * from './lgParser';
|
||||
export * from './templateParser';
|
||||
export * from './generated';
|
||||
export * from './staticChecker';
|
||||
export * from './analyzer';
|
||||
export * from './lgTemplate';
|
||||
export * from './template';
|
||||
export * from './diagnostic';
|
||||
export * from './lgException';
|
||||
export * from './templateException';
|
||||
export * from './extractor';
|
||||
export * from './lgImport';
|
||||
export * from './templateImport';
|
||||
export * from './range';
|
||||
export * from './position';
|
||||
export * from './evaluationTarget';
|
||||
export * from './activityFactory';
|
||||
export * from './activityChecker';
|
||||
export * from './lgExtensions';
|
||||
export * from './templateExtensions';
|
||||
export * from './analyzerResult';
|
||||
export * from './lgErrors';
|
||||
export * from './templateErrors';
|
||||
export * from './evaluator';
|
||||
export * from './errorListener';
|
||||
export * from './customizedMemory';
|
||||
export * from './expander';
|
||||
export * from './multiLanguageLG';
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
import { Templates } from './templates';
|
||||
|
||||
/**
|
||||
* @module botbuilder-lg
|
||||
*/
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
export class MultiLanguageLG {
|
||||
public languageFallbackPolicy: {};
|
||||
public lgPerLocale: Map<string, Templates>;
|
||||
|
||||
private readonly locales = [
|
||||
'','aa','aa-dj','aa-er','aa-et','af','af-na','af-za','agq','agq-cm','ak','ak-gh','am','am-et','ar','ar-001',
|
||||
'ar-ae','ar-bh','ar-dj','ar-dz','ar-eg','ar-er','ar-il','ar-iq','ar-jo','ar-km','ar-kw','ar-lb','ar-ly','ar-ma',
|
||||
'ar-mr','ar-om','ar-ps','ar-qa','ar-sa','ar-sd','ar-so','ar-ss','ar-sy','ar-td','ar-tn','ar-ye','arn','arn-cl',
|
||||
'as','as-in','asa','asa-tz','ast','ast-es','az','az-cyrl','az-cyrl-az','az-latn','az-latn-az','ba','ba-ru','bas',
|
||||
'bas-cm','be','be-by','bem','bem-zm','bez','bez-tz','bg','bg-bg','bin','bin-ng','bm','bm-latn','bm-latn-ml','bn',
|
||||
'bn-bd','bn-in','bo','bo-cn','bo-in','br','br-fr','brx','brx-in','bs','bs-cyrl','bs-cyrl-ba','bs-latn',
|
||||
'bs-latn-ba','byn','byn-er','ca','ca-ad','ca-es','ca-es-valencia','ca-fr','ca-it','ce','ce-ru','cgg','cgg-ug',
|
||||
'chr','chr-cher','chr-cher-us','co','co-fr','cs','cs-cz','cu','cu-ru','cy','cy-gb','da','da-dk','da-gl','dav',
|
||||
'dav-ke','de','de-at','de-be','de-ch','de-de','de-it','de-li','de-lu','dje','dje-ne','dsb','dsb-de','dua',
|
||||
'dua-cm','dv','dv-mv','dyo','dyo-sn','dz','dz-bt','ebu','ebu-ke','ee','ee-gh','ee-tg','el','el-cy','el-gr','en',
|
||||
'en-001','en-029','en-150','en-ag','en-ai','en-as','en-at','en-au','en-bb','en-be','en-bi','en-bm','en-bs',
|
||||
'en-bw','en-bz','en-ca','en-cc','en-ch','en-ck','en-cm','en-cx','en-cy','en-de','en-dk','en-dm','en-er','en-fi',
|
||||
'en-fj','en-fk','en-fm','en-gb','en-gd','en-gg','en-gh','en-gi','en-gm','en-gu','en-gy','en-hk','en-id','en-ie',
|
||||
'en-il','en-im','en-in','en-io','en-je','en-jm','en-ke','en-ki','en-kn','en-ky','en-lc','en-lr','en-ls','en-mg',
|
||||
'en-mh','en-mo','en-mp','en-ms','en-mt','en-mu','en-mw','en-my','en-na','en-nf','en-ng','en-nl','en-nr','en-nu',
|
||||
'en-nz','en-pg','en-ph','en-pk','en-pn','en-pr','en-pw','en-rw','en-sb','en-sc','en-sd','en-se','en-sg','en-sh',
|
||||
'en-si','en-sl','en-ss','en-sx','en-sz','en-tc','en-tk','en-to','en-tt','en-tv','en-tz','en-ug','en-um','en-us',
|
||||
'en-vc','en-vg','en-vi','en-vu','en-ws','en-za','en-zm','en-zw','eo','eo-001','es','es-419','es-ar','es-bo',
|
||||
'es-br','es-bz','es-cl','es-co','es-cr','es-cu','es-do','es-ec','es-es','es-gq','es-gt','es-hn','es-mx','es-ni',
|
||||
'es-pa','es-pe','es-ph','es-pr','es-py','es-sv','es-us','es-uy','es-ve','et','et-ee','eu','eu-es','ewo','ewo-cm',
|
||||
'fa','fa-ir','ff','ff-latn','ff-latn-bf','ff-latn-cm','ff-latn-gh','ff-latn-gm','ff-latn-gn','ff-latn-gw',
|
||||
'ff-latn-lr','ff-latn-mr','ff-latn-ne','ff-latn-ng','ff-latn-sl','ff-latn-sn','fi','fi-fi','fil','fil-ph','fo',
|
||||
'fo-dk','fo-fo','fr','fr-029','fr-be','fr-bf','fr-bi','fr-bj','fr-bl','fr-ca','fr-cd','fr-cf','fr-cg','fr-ch',
|
||||
'fr-ci','fr-cm','fr-dj','fr-dz','fr-fr','fr-ga','fr-gf','fr-gn','fr-gp','fr-gq','fr-ht','fr-km','fr-lu','fr-ma',
|
||||
'fr-mc','fr-mf','fr-mg','fr-ml','fr-mq','fr-mr','fr-mu','fr-nc','fr-ne','fr-pf','fr-pm','fr-re','fr-rw','fr-sc',
|
||||
'fr-sn','fr-sy','fr-td','fr-tg','fr-tn','fr-vu','fr-wf','fr-yt','fur','fur-it','fy','fy-nl','ga','ga-ie','gd',
|
||||
'gd-gb','gl','gl-es','gn','gn-py','gsw','gsw-ch','gsw-fr','gsw-li','gu','gu-in','guz','guz-ke','gv','gv-im','ha',
|
||||
'ha-latn','ha-latn-gh','ha-latn-ne','ha-latn-ng','haw','haw-us','he','he-il','hi','hi-in','hr','hr-ba','hr-hr',
|
||||
'hsb','hsb-de','hu','hu-hu','hy','hy-am','ia','ia-001','ibb','ibb-ng','id','id-id','ig','ig-ng','ii','ii-cn',
|
||||
'is','is-is','it','it-ch','it-it','it-sm','it-va','iu','iu-cans','iu-cans-ca','iu-latn','iu-latn-ca','ja',
|
||||
'ja-jp','jgo','jgo-cm','jmc','jmc-tz','jv','jv-java','jv-java-id','jv-latn','jv-latn-id','ka','ka-ge','kab',
|
||||
'kab-dz','kam','kam-ke','kde','kde-tz','kea','kea-cv','khq','khq-ml','ki','ki-ke','kk','kk-kz','kkj','kkj-cm',
|
||||
'kl','kl-gl','kln','kln-ke','km','km-kh','kn','kn-in','ko','ko-kp','ko-kr','kok','kok-in','kr','kr-latn',
|
||||
'kr-latn-ng','ks','ks-arab','ks-arab-in','ks-deva','ks-deva-in','ksb','ksb-tz','ksf','ksf-cm','ksh','ksh-de',
|
||||
'ku','ku-arab','ku-arab-iq','ku-arab-ir','kw','kw-gb','ky','ky-kg','la','la-001','lag','lag-tz','lb','lb-lu',
|
||||
'lg','lg-ug','lkt','lkt-us','ln','ln-ao','ln-cd','ln-cf','ln-cg','lo','lo-la','lrc','lrc-iq','lrc-ir','lt',
|
||||
'lt-lt','lu','lu-cd','luo','luo-ke','luy','luy-ke','lv','lv-lv','mas','mas-ke','mas-tz','mer','mer-ke','mfe',
|
||||
'mfe-mu','mg','mg-mg','mgh','mgh-mz','mgo','mgo-cm','mi','mi-nz','mk','mk-mk','ml','ml-in','mn','mn-cyrl',
|
||||
'mn-mn','mn-mong','mn-mong-cn','mn-mong-mn','mni','mni-in','moh','moh-ca','mr','mr-in','ms','ms-bn','ms-my',
|
||||
'ms-sg','mt','mt-mt','mua','mua-cm','my','my-mm','mzn','mzn-ir','naq','naq-na','nb','nb-no','nb-sj','nd','nd-zw',
|
||||
'nds','nds-de','nds-nl','ne','ne-in','ne-np','nl','nl-aw','nl-be','nl-bq','nl-cw','nl-nl','nl-sr','nl-sx','nmg',
|
||||
'nmg-cm','nn','nn-no','nnh','nnh-cm','no','nqo','nqo-gn','nr','nr-za','nso','nso-za','nus','nus-ss','nyn',
|
||||
'nyn-ug','oc','oc-fr','om','om-et','om-ke','or','or-in','os','os-ge','os-ru','pa','pa-arab','pa-arab-pk',
|
||||
'pa-guru','pa-in','pap','pap-029','pl','pl-pl','prg','prg-001','prs','prs-af','ps','ps-af','pt','pt-ao','pt-br',
|
||||
'pt-ch','pt-cv','pt-gq','pt-gw','pt-lu','pt-mo','pt-mz','pt-pt','pt-st','pt-tl','quc','quc-latn','quc-latn-gt',
|
||||
'quz','quz-bo','quz-ec','quz-pe','rm','rm-ch','rn','rn-bi','ro','ro-md','ro-ro','rof','rof-tz','ru','ru-by',
|
||||
'ru-kg','ru-kz','ru-md','ru-ru','ru-ua','rw','rw-rw','rwk','rwk-tz','sa','sa-in','sah','sah-ru','saq','saq-ke',
|
||||
'sbp','sbp-tz','sd','sd-arab','sd-arab-pk','sd-deva','sd-deva-in','se','se-fi','se-no','se-se','seh','seh-mz',
|
||||
'ses','ses-ml','sg','sg-cf','shi','shi-latn','shi-latn-ma','shi-tfng','shi-tfng-ma','si','si-lk','sk','sk-sk',
|
||||
'sl','sl-si','sma','sma-no','sma-se','smj','smj-no','smj-se','smn','smn-fi','sms','sms-fi','sn','sn-latn',
|
||||
'sn-latn-zw','so','so-dj','so-et','so-ke','so-so','sq','sq-al','sq-mk','sq-xk','sr','sr-cyrl','sr-cyrl-ba',
|
||||
'sr-cyrl-me','sr-cyrl-rs','sr-cyrl-xk','sr-latn','sr-latn-ba','sr-latn-me','sr-latn-rs','sr-latn-xk','ss',
|
||||
'ss-sz','ss-za','ssy','ssy-er','st','st-ls','st-za','sv','sv-ax','sv-fi','sv-se','sw','sw-cd','sw-ke','sw-tz',
|
||||
'sw-ug','syr','syr-sy','ta','ta-in','ta-lk','ta-my','ta-sg','te','te-in','teo','teo-ke','teo-ug','tg','tg-cyrl',
|
||||
'tg-cyrl-tj','th','th-th','ti','ti-er','ti-et','tig','tig-er','tk','tk-tm','tn','tn-bw','tn-za','to','to-to',
|
||||
'tr','tr-cy','tr-tr','ts','ts-za','tt','tt-ru','twq','twq-ne','tzm','tzm-arab','tzm-arab-ma','tzm-latn',
|
||||
'tzm-latn-dz','tzm-latn-ma','tzm-tfng','tzm-tfng-ma','ug','ug-cn','uk','uk-ua','ur','ur-in','ur-pk','uz',
|
||||
'uz-arab','uz-arab-af','uz-cyrl','uz-cyrl-uz','uz-latn','uz-latn-uz','vai','vai-latn','vai-latn-lr','vai-vaii',
|
||||
'vai-vaii-lr','ve','ve-za','vi','vi-vn','vo','vo-001','vun','vun-tz','wae','wae-ch','wal','wal-et','wo','wo-sn',
|
||||
'xh','xh-za','xog','xog-ug','yav','yav-cm','yi','yi-001','yo','yo-bj','yo-ng','zgh','zgh-tfng','zgh-tfng-ma',
|
||||
'zh','zh-cn','zh-hans','zh-hans-hk','zh-hans-mo','zh-hant','zh-hk','zh-mo','zh-sg','zh-tw','zu','zu-za'];
|
||||
|
||||
public constructor(localeLGFiles: Map<string, string>) {
|
||||
this.lgPerLocale = new Map<string, Templates>();
|
||||
this.languageFallbackPolicy = this.getDefaultPolicy();
|
||||
|
||||
if (!localeLGFiles) {
|
||||
throw new Error(`input is empty`);
|
||||
}
|
||||
|
||||
for (const filesPerLocale of localeLGFiles.entries()) {
|
||||
this.lgPerLocale.set(filesPerLocale[0], Templates.parseFile(filesPerLocale[1]));
|
||||
}
|
||||
}
|
||||
|
||||
public generate(template: string, data?: object, locale?: string): any {
|
||||
if (!template) {
|
||||
throw new Error('template is empty');
|
||||
}
|
||||
|
||||
if (!locale) {
|
||||
locale = '';
|
||||
}
|
||||
|
||||
if (this.lgPerLocale.has(locale)) {
|
||||
return this.lgPerLocale.get(locale).evaluateText(template, data);
|
||||
} else {
|
||||
let locales: string[] = [''];
|
||||
if (!(locale in this.languageFallbackPolicy)) {
|
||||
if (!('' in this.languageFallbackPolicy)) {
|
||||
throw Error(`No supported language found for ${ locale }`);
|
||||
}
|
||||
} else {
|
||||
locales = this.languageFallbackPolicy[locale];
|
||||
}
|
||||
|
||||
for (const fallBackLocale of locales) {
|
||||
if (this.lgPerLocale.has(fallBackLocale)) {
|
||||
return this.lgPerLocale.get(fallBackLocale).evaluateText(template, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`No LG responses found for locale: ${ locale }`);
|
||||
|
||||
}
|
||||
|
||||
private getDefaultPolicy(): any {
|
||||
var result = {};
|
||||
for (const locale of this.locales) {
|
||||
let lang = locale.toLowerCase();
|
||||
const fallback: string[] = [];
|
||||
while (lang) {
|
||||
fallback.push(lang);
|
||||
const i = lang.lastIndexOf('-');
|
||||
if (i > 0) {
|
||||
lang = lang.substr(0, i);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fallback.push('');
|
||||
result[locale] = fallback;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -12,24 +12,24 @@ import { Diagnostic, DiagnosticSeverity } from './diagnostic';
|
|||
import { Evaluator } from './evaluator';
|
||||
import * as lp from './generated/LGFileParser';
|
||||
import { LGFileParserVisitor } from './generated/LGFileParserVisitor';
|
||||
import { LGFile } from './lgFile';
|
||||
import { LGErrors } from './lgErrors';
|
||||
import { Templates } from './templates';
|
||||
import { TemplateErrors } from './templateErrors';
|
||||
import { Position } from './position';
|
||||
import { Range } from './range';
|
||||
import { LGExtensions } from './lgExtensions';
|
||||
import { TemplateExtensions } from './templateExtensions';
|
||||
|
||||
/// <summary>
|
||||
/// LG managed code checker.
|
||||
/// </summary>
|
||||
export class StaticChecker extends AbstractParseTreeVisitor<Diagnostic[]> implements LGFileParserVisitor<Diagnostic[]> {
|
||||
private readonly baseExpressionParser: ExpressionParser;
|
||||
private readonly lgFile: LGFile;
|
||||
private readonly templates: Templates;
|
||||
private visitedTemplateNames: string[];
|
||||
private _expressionParser: ExpressionParserInterface;
|
||||
|
||||
public constructor(lgFile: LGFile, expressionParser?: ExpressionParser) {
|
||||
public constructor(templates: Templates, expressionParser?: ExpressionParser) {
|
||||
super();
|
||||
this.lgFile = lgFile;
|
||||
this.templates = templates;
|
||||
this.baseExpressionParser = expressionParser || new ExpressionParser();
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ export class StaticChecker extends AbstractParseTreeVisitor<Diagnostic[]> implem
|
|||
private get expressionParser(): ExpressionParserInterface {
|
||||
if (this._expressionParser === undefined) {
|
||||
// create an evaluator to leverage it's customized function look up for checking
|
||||
var evaluator = new Evaluator(this.lgFile.allTemplates, this.baseExpressionParser);
|
||||
var evaluator = new Evaluator(this.templates.allTemplates, this.baseExpressionParser);
|
||||
this._expressionParser = evaluator.expressionParser;
|
||||
}
|
||||
|
||||
|
@ -52,14 +52,14 @@ export class StaticChecker extends AbstractParseTreeVisitor<Diagnostic[]> implem
|
|||
this.visitedTemplateNames = [];
|
||||
var result = [];
|
||||
|
||||
if (this.lgFile.allTemplates.length === 0)
|
||||
if (this.templates.allTemplates.length === 0)
|
||||
{
|
||||
result.push(this.buildLGDiagnostic(LGErrors.noTemplate, DiagnosticSeverity.Warning, undefined, false));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.noTemplate, DiagnosticSeverity.Warning, undefined, false));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
this.lgFile.templates.forEach((t): Diagnostic[] => result = result.concat(this.visit(t.parseTree)));
|
||||
this.templates.toArray().forEach((t): Diagnostic[] => result = result.concat(this.visit(t.parseTree)));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -69,25 +69,25 @@ export class StaticChecker extends AbstractParseTreeVisitor<Diagnostic[]> implem
|
|||
var templateNameLine = context.templateNameLine();
|
||||
var errorTemplateName = templateNameLine.errorTemplateName();
|
||||
if (errorTemplateName) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.invalidTemplateName, undefined, errorTemplateName, false));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.invalidTemplateName, undefined, errorTemplateName, false));
|
||||
} else {
|
||||
var templateName = context.templateNameLine().templateName().text;
|
||||
|
||||
if (this.visitedTemplateNames.includes(templateName)) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.duplicatedTemplateInSameTemplate(templateName),undefined, templateNameLine));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.duplicatedTemplateInSameTemplate(templateName),undefined, templateNameLine));
|
||||
} else {
|
||||
this.visitedTemplateNames.push(templateName);
|
||||
for (const reference of this.lgFile.references) {
|
||||
var sameTemplates = reference.templates.filter((u): boolean => u.name === templateName);
|
||||
for (const reference of this.templates.references) {
|
||||
var sameTemplates = reference.toArray().filter((u): boolean => u.name === templateName);
|
||||
for(const sameTemplate of sameTemplates) {
|
||||
result.push(this.buildLGDiagnostic( LGErrors.duplicatedTemplateInDiffTemplate(sameTemplate.name, sameTemplate.source), undefined, templateNameLine));
|
||||
result.push(this.buildLGDiagnostic( TemplateErrors.duplicatedTemplateInDiffTemplate(sameTemplate.name, sameTemplate.source), undefined, templateNameLine));
|
||||
}
|
||||
}
|
||||
|
||||
if (result.length > 0) {
|
||||
return result;
|
||||
} else if (!context.templateBody()) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.noTemplateBody(templateName), DiagnosticSeverity.Warning, context.templateNameLine()));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.noTemplateBody(templateName), DiagnosticSeverity.Warning, context.templateNameLine()));
|
||||
} else {
|
||||
result = result.concat(this.visit(context.templateBody()));
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ export class StaticChecker extends AbstractParseTreeVisitor<Diagnostic[]> implem
|
|||
const errorTemplateStr: lp.ErrorTemplateStringContext = templateStr.errorTemplateString();
|
||||
|
||||
if (errorTemplateStr) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.invalidTemplateBody, undefined, errorTemplateStr));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.invalidTemplateBody, undefined, errorTemplateStr));
|
||||
} else {
|
||||
result = result.concat(this.visit(templateStr));
|
||||
}
|
||||
|
@ -116,20 +116,20 @@ export class StaticChecker extends AbstractParseTreeVisitor<Diagnostic[]> implem
|
|||
let result: Diagnostic[] = [];
|
||||
|
||||
if (context.structuredBodyNameLine().errorStructuredName() !== undefined) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.invalidStrucName, undefined, context.structuredBodyNameLine()));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.invalidStrucName, undefined, context.structuredBodyNameLine()));
|
||||
}
|
||||
|
||||
if (context.structuredBodyEndLine() === undefined) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.missingStrucEnd, undefined, context));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.missingStrucEnd, undefined, context));
|
||||
}
|
||||
|
||||
const bodys = context.structuredBodyContentLine();
|
||||
if (!bodys || bodys.length === 0) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.emptyStrucContent, undefined, context));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.emptyStrucContent, undefined, context));
|
||||
} else {
|
||||
for (const body of bodys) {
|
||||
if (body.errorStructureLine() !== undefined) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.invalidStrucBody, undefined, body.errorStructureLine()));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.invalidStrucBody, undefined, body.errorStructureLine()));
|
||||
} else if (body.objectStructureLine() !== undefined) {
|
||||
result = result.concat(this.checkExpression(body.objectStructureLine().text, body.objectStructureLine()));
|
||||
} else {
|
||||
|
@ -163,41 +163,41 @@ export class StaticChecker extends AbstractParseTreeVisitor<Diagnostic[]> implem
|
|||
conditionNode.ELSE();
|
||||
|
||||
if (node.text.split(' ').length - 1 > 1) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.invalidWhitespaceInCondition, undefined, conditionNode));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.invalidWhitespaceInCondition, undefined, conditionNode));
|
||||
}
|
||||
|
||||
if (idx === 0 && !ifExpr) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.notStartWithIfInCondition, DiagnosticSeverity.Warning, conditionNode));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.notStartWithIfInCondition, DiagnosticSeverity.Warning, conditionNode));
|
||||
}
|
||||
|
||||
if (idx > 0 && ifExpr) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.multipleIfInCondition, undefined, conditionNode));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.multipleIfInCondition, undefined, conditionNode));
|
||||
}
|
||||
|
||||
if (idx === ifRules.length - 1 && !elseExpr) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.notEndWithElseInCondition, DiagnosticSeverity.Warning, conditionNode));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.notEndWithElseInCondition, DiagnosticSeverity.Warning, conditionNode));
|
||||
}
|
||||
|
||||
if (idx > 0 && idx < ifRules.length - 1 && !elseIfExpr) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.invalidMiddleInCondition, undefined, conditionNode));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.invalidMiddleInCondition, undefined, conditionNode));
|
||||
}
|
||||
|
||||
if (!elseExpr) {
|
||||
if (ifRule.ifCondition().EXPRESSION().length !== 1) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.invalidExpressionInCondition,undefined, conditionNode));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.invalidExpressionInCondition,undefined, conditionNode));
|
||||
} else {
|
||||
const errorPrefix = `Condition '` + conditionNode.EXPRESSION(0).text + `': `;
|
||||
result = result.concat(this.checkExpression(ifRule.ifCondition().EXPRESSION(0).text, conditionNode, errorPrefix));
|
||||
}
|
||||
} else {
|
||||
if (ifRule.ifCondition().EXPRESSION().length !== 0) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.extraExpressionInCondition, undefined, conditionNode));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.extraExpressionInCondition, undefined, conditionNode));
|
||||
}
|
||||
}
|
||||
if (ifRule.normalTemplateBody() !== undefined) {
|
||||
result = result.concat(this.visit(ifRule.normalTemplateBody()));
|
||||
} else {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.missingTemplateBodyInCondition, undefined, conditionNode));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.missingTemplateBodyInCondition, undefined, conditionNode));
|
||||
}
|
||||
|
||||
idx = idx + 1;
|
||||
|
@ -221,33 +221,33 @@ export class StaticChecker extends AbstractParseTreeVisitor<Diagnostic[]> implem
|
|||
caseExpr ? switchCaseStat.CASE() :
|
||||
switchCaseStat.DEFAULT();
|
||||
if (node.text.split(' ').length - 1 > 1) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.invalidWhitespaceInSwitchCase, undefined, switchCaseStat));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.invalidWhitespaceInSwitchCase, undefined, switchCaseStat));
|
||||
}
|
||||
|
||||
if (idx === 0 && !switchExpr) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.notStartWithSwitchInSwitchCase, undefined, switchCaseStat));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.notStartWithSwitchInSwitchCase, undefined, switchCaseStat));
|
||||
}
|
||||
|
||||
if (idx > 0 && switchExpr) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.multipleSwithStatementInSwitchCase, undefined, switchCaseStat));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.multipleSwithStatementInSwitchCase, undefined, switchCaseStat));
|
||||
}
|
||||
|
||||
if (idx > 0 && idx < length - 1 && !caseExpr) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.invalidStatementInMiddlerOfSwitchCase, undefined, switchCaseStat));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.invalidStatementInMiddlerOfSwitchCase, undefined, switchCaseStat));
|
||||
}
|
||||
|
||||
if (idx === length - 1 && (caseExpr || defaultExpr)) {
|
||||
if (caseExpr) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.notEndWithDefaultInSwitchCase, DiagnosticSeverity.Warning, switchCaseStat));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.notEndWithDefaultInSwitchCase, DiagnosticSeverity.Warning, switchCaseStat));
|
||||
} else {
|
||||
if (length === 2) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.missingCaseInSwitchCase, DiagnosticSeverity.Warning, switchCaseStat));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.missingCaseInSwitchCase, DiagnosticSeverity.Warning, switchCaseStat));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (switchExpr || caseExpr) {
|
||||
if (switchCaseStat.EXPRESSION().length !== 1) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.invalidExpressionInSwiathCase, undefined, switchCaseStat));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.invalidExpressionInSwiathCase, undefined, switchCaseStat));
|
||||
} else {
|
||||
let errorPrefix = switchExpr ? 'Switch' : 'Case';
|
||||
errorPrefix += ` '${ switchCaseStat.EXPRESSION(0).text }': `;
|
||||
|
@ -255,14 +255,14 @@ export class StaticChecker extends AbstractParseTreeVisitor<Diagnostic[]> implem
|
|||
}
|
||||
} else {
|
||||
if (switchCaseStat.EXPRESSION().length !== 0 || switchCaseStat.TEXT().length !== 0) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.extraExpressionInSwitchCase, undefined, switchCaseStat));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.extraExpressionInSwitchCase, undefined, switchCaseStat));
|
||||
}
|
||||
}
|
||||
if (caseExpr || defaultExpr) {
|
||||
if (iterNode.normalTemplateBody()) {
|
||||
result = result.concat(this.visit(iterNode.normalTemplateBody()));
|
||||
} else {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.missingTemplateBodyInSwitchCase, undefined, switchCaseStat));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.missingTemplateBodyInSwitchCase, undefined, switchCaseStat));
|
||||
}
|
||||
}
|
||||
idx = idx + 1;
|
||||
|
@ -272,7 +272,7 @@ export class StaticChecker extends AbstractParseTreeVisitor<Diagnostic[]> implem
|
|||
}
|
||||
|
||||
public visitNormalTemplateString(context: lp.NormalTemplateStringContext): Diagnostic[] {
|
||||
const prefixErrorMsg = LGExtensions.getPrefixErrorMessage(context);
|
||||
const prefixErrorMsg = TemplateExtensions.getPrefixErrorMessage(context);
|
||||
let result: Diagnostic[] = [];
|
||||
|
||||
for (const expression of context.EXPRESSION()) {
|
||||
|
@ -283,7 +283,7 @@ export class StaticChecker extends AbstractParseTreeVisitor<Diagnostic[]> implem
|
|||
const multiLineSuffix = context.MULTILINE_SUFFIX();
|
||||
|
||||
if (multiLinePrefix !== undefined && multiLineSuffix === undefined) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.noEndingInMultiline, undefined, context));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.noEndingInMultiline, undefined, context));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -295,14 +295,14 @@ export class StaticChecker extends AbstractParseTreeVisitor<Diagnostic[]> implem
|
|||
private checkExpression(exp: string, context: ParserRuleContext, prefix: string = ''): Diagnostic[] {
|
||||
const result: Diagnostic[] = [];
|
||||
if(!exp.endsWith('}')) {
|
||||
result.push(this.buildLGDiagnostic(LGErrors.noCloseBracket, undefined, context));
|
||||
result.push(this.buildLGDiagnostic(TemplateErrors.noCloseBracket, undefined, context));
|
||||
} else {
|
||||
exp = LGExtensions.trimExpression(exp);
|
||||
exp = TemplateExtensions.trimExpression(exp);
|
||||
|
||||
try {
|
||||
this.expressionParser.parse(exp);
|
||||
} catch (e) {
|
||||
const errorMsg = prefix + LGErrors.expressionParseError(exp) + e.message;
|
||||
const errorMsg = prefix + TemplateErrors.expressionParseError(exp) + e.message;
|
||||
result.push(this.buildLGDiagnostic(errorMsg, undefined, context));
|
||||
|
||||
return result;
|
||||
|
@ -319,6 +319,6 @@ export class StaticChecker extends AbstractParseTreeVisitor<Diagnostic[]> implem
|
|||
const range = new Range(startPosition, stopPosition);
|
||||
message = (this.visitedTemplateNames.length > 0 && includeTemplateNameInfo)? `[${ this.visitedTemplateNames[this.visitedTemplateNames.length - 1] }]`+ message : message;
|
||||
|
||||
return new Diagnostic(range, message, severity, this.lgFile.id);
|
||||
return new Diagnostic(range, message, severity, this.templates.id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import { ParametersContext, TemplateDefinitionContext, TemplateNameContext, File
|
|||
* Here is a data model that can easily understanded and used as the context or all kinds of visitors
|
||||
* wether it's evalator, static checker, anayler.. etc
|
||||
*/
|
||||
export class LGTemplate {
|
||||
export class Template {
|
||||
/**
|
||||
* Name of the template, what's followed by '#' in a LG file
|
||||
*/
|
|
@ -9,7 +9,7 @@
|
|||
/**
|
||||
* Centralized LG errors.
|
||||
*/
|
||||
export class LGErrors {
|
||||
export class TemplateErrors {
|
||||
public static readonly noTemplate: string = `LG file must have at least one template definition. `;
|
||||
|
||||
public static readonly invalidTemplateName: string = `Invalid template name. Template name should start with letter/number/_ and can only contains letter/number/_/./-. `;
|
|
@ -10,13 +10,13 @@ import { Diagnostic } from './diagnostic';
|
|||
/**
|
||||
* LG Exception that contains diagnostics.
|
||||
*/
|
||||
export class LGException extends Error {
|
||||
export class TemplateException extends Error {
|
||||
|
||||
private diagnostics: Diagnostic[];
|
||||
public constructor(m: string, diagnostics: Diagnostic[]) {
|
||||
super(m);
|
||||
this.diagnostics = diagnostics;
|
||||
Object.setPrototypeOf(this, LGException .prototype);
|
||||
Object.setPrototypeOf(this, TemplateException .prototype);
|
||||
}
|
||||
|
||||
/**
|
|
@ -12,7 +12,7 @@ import { TerminalNode } from 'antlr4ts/tree';
|
|||
/**
|
||||
* Extension methods for LG.
|
||||
*/
|
||||
export class LGExtensions {
|
||||
export class TemplateExtensions {
|
||||
|
||||
/**
|
||||
* trim expression. ${abc} => abc, ${a == {}} => a == {}.
|
|
@ -11,7 +11,7 @@ import { ImportDefinitionContext } from './generated/LGFileParser';
|
|||
/**
|
||||
* Here is a data model that can help users understand and use the LG import definition in LG files easily.
|
||||
*/
|
||||
export class LGImport {
|
||||
export class TemplateImport {
|
||||
|
||||
/**
|
||||
* Description of the import, what's included by '[]' in a lg file.
|
|
@ -10,12 +10,12 @@ import { CommonTokenStream } from 'antlr4ts/CommonTokenStream';
|
|||
import { ErrorListener } from './errorListener';
|
||||
import { LGFileLexer } from './generated/LGFileLexer';
|
||||
import { FileContext, ImportDefinitionContext, LGFileParser, ParagraphContext, TemplateDefinitionContext, OptionsDefinitionContext } from './generated/LGFileParser';
|
||||
import { LGImport } from './lgImport';
|
||||
import { LGTemplate } from './lgTemplate';
|
||||
import { LGFile } from './lgFile';
|
||||
import { TemplateImport } from './templateImport';
|
||||
import { Template } from './template';
|
||||
import { Templates } from './templates';
|
||||
import { StaticChecker } from './staticChecker';
|
||||
import { LGExtensions } from './lgExtensions';
|
||||
import { LGException } from './lgException';
|
||||
import { TemplateExtensions } from './templateExtensions';
|
||||
import { TemplateException } from './templateException';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { Diagnostic, DiagnosticSeverity } from './diagnostic';
|
||||
|
@ -29,7 +29,7 @@ export declare type ImportResolverDelegate = (source: string, resourceId: string
|
|||
/**
|
||||
* LG Parser
|
||||
*/
|
||||
export class LGParser {
|
||||
export class TemplateParser {
|
||||
|
||||
/// <summary>
|
||||
/// option regex.
|
||||
|
@ -43,70 +43,69 @@ export class LGParser {
|
|||
* @param expressionParser Expression parser for evaluating expressions.
|
||||
* @returns new lg file.
|
||||
*/
|
||||
public static parseFile(filePath: string, importResolver?: ImportResolverDelegate, expressionParser?: ExpressionParser): LGFile {
|
||||
const fullPath = LGExtensions.normalizePath(filePath);
|
||||
public static parseFile(filePath: string, importResolver?: ImportResolverDelegate, expressionParser?: ExpressionParser): Templates {
|
||||
const fullPath = TemplateExtensions.normalizePath(filePath);
|
||||
const content = fs.readFileSync(fullPath, 'utf-8');
|
||||
|
||||
return LGParser.parseText(content, fullPath, importResolver, expressionParser);
|
||||
return TemplateParser.parseText(content, fullPath, importResolver, expressionParser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parser to turn lg content into a LGFile.
|
||||
* Parser to turn lg content into a Templates.
|
||||
* @param content text content contains lg templates.
|
||||
* @param id id is the identifier of content. If importResolver is undefined, id must be a full path string.
|
||||
* @param importResolver resolver to resolve LG import id to template text.
|
||||
* @param expressionParser Expression parser for evaluating expressions.
|
||||
* @returns entity.
|
||||
*/
|
||||
public static parseText(content: string, id: string = '', importResolver?: ImportResolverDelegate, expressionParser?: ExpressionParser): LGFile {
|
||||
importResolver = importResolver || LGParser.defaultFileResolver;
|
||||
let lgFile = new LGFile();
|
||||
lgFile.content = content;
|
||||
lgFile.id = id;
|
||||
lgFile.importResolver = importResolver;
|
||||
public static parseText(content: string, id: string = '', importResolver?: ImportResolverDelegate, expressionParser?: ExpressionParser): Templates {
|
||||
importResolver = importResolver || TemplateParser.defaultFileResolver;
|
||||
let templates = new Templates();
|
||||
templates.content = content;
|
||||
templates.id = id;
|
||||
templates.importResolver = importResolver;
|
||||
if (expressionParser) {
|
||||
lgFile.expressionParser = expressionParser;
|
||||
templates.expressionParser = expressionParser;
|
||||
}
|
||||
let diagnostics: Diagnostic[] = [];
|
||||
try {
|
||||
const parsedResult = LGParser.antlrParse(content, id);
|
||||
lgFile.templates = parsedResult.templates;
|
||||
lgFile.imports = parsedResult.imports;
|
||||
lgFile.options = parsedResult.options;
|
||||
const parsedResult = TemplateParser.antlrParse(content, id);
|
||||
parsedResult.templates.forEach(t => templates.push(t));
|
||||
templates.imports = parsedResult.imports;
|
||||
templates.options = parsedResult.options;
|
||||
|
||||
diagnostics = diagnostics.concat(parsedResult.invalidTemplateErrors);
|
||||
lgFile.references = this.getReferences(lgFile, importResolver);
|
||||
const semanticErrors = new StaticChecker(lgFile, lgFile.expressionParser).check();
|
||||
templates.references = this.getReferences(templates, importResolver);
|
||||
const semanticErrors = new StaticChecker(templates, templates.expressionParser).check();
|
||||
diagnostics = diagnostics.concat(semanticErrors);
|
||||
} catch (err) {
|
||||
if (err instanceof LGException) {
|
||||
if (err instanceof TemplateException) {
|
||||
diagnostics = diagnostics.concat(err.getDiagnostic());
|
||||
} else {
|
||||
diagnostics.push(this.buildDiagnostic(err.Message, undefined, id));
|
||||
}
|
||||
}
|
||||
|
||||
lgFile.diagnostics = diagnostics;
|
||||
templates.diagnostics = diagnostics;
|
||||
|
||||
return lgFile;
|
||||
return templates;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parser to turn lg content into a LGFile based on the original LGFile.
|
||||
/// </summary>
|
||||
/// <param name="content">Text content contains lg templates.</param>
|
||||
/// <param name="lgFile">original LGFile.</param>
|
||||
/// <returns>new LGFile entity.</returns>
|
||||
public static parseTextWithRef(content: string, lgFile: LGFile): LGFile {
|
||||
if (!lgFile) {
|
||||
throw Error(`LGFile`);
|
||||
/**
|
||||
* Parser to turn lg content into a Templates based on the original Templates.
|
||||
* @param content Text content contains lg templates.
|
||||
* @param originalTemplates original templates
|
||||
*/
|
||||
public static parseTextWithRef(content: string, originalTemplates: Templates): Templates {
|
||||
if (!originalTemplates) {
|
||||
throw Error(`templates is empty`);
|
||||
}
|
||||
|
||||
const id = 'inline content';
|
||||
let newLgFile = new LGFile();
|
||||
newLgFile.content = content;
|
||||
newLgFile.id = id;
|
||||
newLgFile.importResolver = lgFile.importResolver;
|
||||
let newTemplates = new Templates();
|
||||
newTemplates.content = content;
|
||||
newTemplates.id = id;
|
||||
newTemplates.importResolver = originalTemplates.importResolver;
|
||||
let diagnostics: Diagnostic[] = [];
|
||||
try {
|
||||
const antlrResult = this.antlrParse(content, id);
|
||||
|
@ -114,36 +113,36 @@ export class LGParser {
|
|||
const imports = antlrResult.imports;
|
||||
const invalidTemplateErrors = antlrResult.invalidTemplateErrors;
|
||||
const options = antlrResult.options;
|
||||
newLgFile.templates = templates;
|
||||
newLgFile.imports = imports;
|
||||
newLgFile.options = options;
|
||||
templates.forEach(t => newTemplates.push(t));
|
||||
newTemplates.imports = imports;
|
||||
newTemplates.options = options;
|
||||
diagnostics = diagnostics.concat(invalidTemplateErrors);
|
||||
|
||||
newLgFile.references = this.getReferences(newLgFile, newLgFile.importResolver)
|
||||
.concat(lgFile.references)
|
||||
.concat([lgFile]);
|
||||
newTemplates.references = this.getReferences(newTemplates, newTemplates.importResolver)
|
||||
.concat(originalTemplates.references)
|
||||
.concat([originalTemplates]);
|
||||
|
||||
var semanticErrors = new StaticChecker(newLgFile).check();
|
||||
var semanticErrors = new StaticChecker(newTemplates).check();
|
||||
diagnostics = diagnostics.concat(semanticErrors);
|
||||
}
|
||||
catch (err) {
|
||||
if (err instanceof LGException) {
|
||||
if (err instanceof TemplateException) {
|
||||
diagnostics = diagnostics.concat(err.getDiagnostic());
|
||||
} else {
|
||||
diagnostics.push(this.buildDiagnostic(err.Message, undefined, id));
|
||||
}
|
||||
}
|
||||
|
||||
newLgFile.diagnostics = diagnostics;
|
||||
newTemplates.diagnostics = diagnostics;
|
||||
|
||||
return newLgFile;
|
||||
return newTemplates;
|
||||
}
|
||||
|
||||
public static defaultFileResolver(sourceId: string, resourceId: string): { content: string; id: string } {
|
||||
let importPath = LGExtensions.normalizePath(resourceId);
|
||||
let importPath = TemplateExtensions.normalizePath(resourceId);
|
||||
if (!path.isAbsolute(importPath)) {
|
||||
// get full path for importPath relative to path which is doing the import.
|
||||
importPath = LGExtensions.normalizePath(path.join(path.dirname(sourceId), importPath));
|
||||
importPath = TemplateExtensions.normalizePath(path.join(path.dirname(sourceId), importPath));
|
||||
}
|
||||
if (!fs.existsSync(importPath) || !fs.statSync(importPath).isFile()) {
|
||||
throw Error(`Could not find file: ${ importPath }`);
|
||||
|
@ -153,25 +152,25 @@ export class LGParser {
|
|||
return { content, id: importPath };
|
||||
}
|
||||
|
||||
private static antlrParse(text: string, id: string = ''): { templates: LGTemplate[]; imports: LGImport[]; invalidTemplateErrors: Diagnostic[]; options: string[]} {
|
||||
private static antlrParse(text: string, id: string = ''): { templates: Template[]; imports: TemplateImport[]; invalidTemplateErrors: Diagnostic[]; options: string[]} {
|
||||
const fileContext: FileContext = this.getFileContentContext(text, id);
|
||||
const templates: LGTemplate[] = this.extractLGTemplates(fileContext, text, id);
|
||||
const imports: LGImport[] = this.extractLGImports(fileContext, id);
|
||||
const templates: Template[] = this.extractLGTemplates(fileContext, text, id);
|
||||
const imports: TemplateImport[] = this.extractLGImports(fileContext, id);
|
||||
const invalidTemplateErrors: Diagnostic[] = this.getInvalidTemplateErrors(fileContext, id);
|
||||
const options: string[] = this.extractLGOptions(fileContext);
|
||||
return { templates, imports, invalidTemplateErrors, options};
|
||||
}
|
||||
|
||||
private static getReferences(file: LGFile, importResolver: ImportResolverDelegate): LGFile[] {
|
||||
var resourcesFound = new Set<LGFile>();
|
||||
private static getReferences(file: Templates, importResolver: ImportResolverDelegate): Templates[] {
|
||||
var resourcesFound = new Set<Templates>();
|
||||
this.resolveImportResources(file, resourcesFound, importResolver);
|
||||
|
||||
resourcesFound.delete(file);
|
||||
return Array.from(resourcesFound);
|
||||
}
|
||||
|
||||
private static resolveImportResources(start: LGFile, resourcesFound: Set<LGFile>, importResolver: ImportResolverDelegate): void {
|
||||
var resourceIds = start.imports.map((lg: LGImport): string => lg.id);
|
||||
private static resolveImportResources(start: Templates, resourcesFound: Set<Templates>, importResolver: ImportResolverDelegate): void {
|
||||
var resourceIds = start.imports.map((lg: TemplateImport): string => lg.id);
|
||||
resourcesFound.add(start);
|
||||
|
||||
for (const id of resourceIds) {
|
||||
|
@ -181,15 +180,15 @@ export class LGParser {
|
|||
const path = result.id;
|
||||
const notExist = Array.from(resourcesFound).filter((u): boolean => u.id === path).length === 0;
|
||||
if (notExist) {
|
||||
var childResource = LGParser.parseText(content, path, importResolver, start.expressionParser);
|
||||
var childResource = TemplateParser.parseText(content, path, importResolver, start.expressionParser);
|
||||
this.resolveImportResources(childResource, resourcesFound, importResolver);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
if (err instanceof LGException) {
|
||||
if (err instanceof TemplateException) {
|
||||
throw err;
|
||||
} else {
|
||||
throw new LGException(err.message, [this.buildDiagnostic(err.message, undefined, start.id)]);
|
||||
throw new TemplateException(err.message, [this.buildDiagnostic(err.message, undefined, start.id)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -263,7 +262,7 @@ export class LGParser {
|
|||
return parser.file();
|
||||
}
|
||||
|
||||
private static extractLGTemplates(file: FileContext, lgfileContent: string, source: string = ''): LGTemplate[] {
|
||||
private static extractLGTemplates(file: FileContext, lgfileContent: string, source: string = ''): Template[] {
|
||||
if (!file) {
|
||||
return [];
|
||||
}
|
||||
|
@ -272,10 +271,10 @@ export class LGParser {
|
|||
.map((x: ParagraphContext): TemplateDefinitionContext => x.templateDefinition())
|
||||
.filter((x: TemplateDefinitionContext): boolean => x !== undefined);
|
||||
|
||||
return templates.map((x: TemplateDefinitionContext): LGTemplate => new LGTemplate(x, lgfileContent, source));
|
||||
return templates.map((x: TemplateDefinitionContext): Template => new Template(x, lgfileContent, source));
|
||||
}
|
||||
|
||||
private static extractLGImports(file: FileContext, source: string = ''): LGImport[] {
|
||||
private static extractLGImports(file: FileContext, source: string = ''): TemplateImport[] {
|
||||
if (!file) {
|
||||
return [];
|
||||
}
|
||||
|
@ -284,6 +283,6 @@ export class LGParser {
|
|||
.map((x: ParagraphContext): ImportDefinitionContext => x.importDefinition())
|
||||
.filter((x: ImportDefinitionContext): boolean => x !== undefined);
|
||||
|
||||
return imports.map((x: ImportDefinitionContext): LGImport => new LGImport(x, source));
|
||||
return imports.map((x: ImportDefinitionContext): TemplateImport => new TemplateImport(x, source));
|
||||
}
|
||||
}
|
|
@ -6,31 +6,24 @@
|
|||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import { LGTemplate } from './lgTemplate';
|
||||
import { LGImport } from './lgImport';
|
||||
import { Template } from './template';
|
||||
import { TemplateImport } from './templateImport';
|
||||
import { Diagnostic, DiagnosticSeverity } from './diagnostic';
|
||||
import { ExpressionParser } from 'adaptive-expressions';
|
||||
import { ImportResolverDelegate } from './lgParser';
|
||||
import { ImportResolverDelegate } from './templateParser';
|
||||
import { Evaluator } from './evaluator';
|
||||
import { Expander } from './expander';
|
||||
import { Analyzer } from './analyzer';
|
||||
import { LGParser } from './lgParser';
|
||||
import { TemplateParser } from './templateParser';
|
||||
import { AnalyzerResult } from './analyzerResult';
|
||||
import { LGErrors } from './lgErrors';
|
||||
import { LGExtensions } from './lgExtensions';
|
||||
import { TemplateErrors } from './templateErrors';
|
||||
import { TemplateExtensions } from './templateExtensions';
|
||||
|
||||
/// <summary>
|
||||
/// LG entrance, including properties that LG file has, and evaluate functions.
|
||||
/// </summary>
|
||||
export class LGFile {
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets templates that this LG file contains directly.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// templates that this LG file contains directly.
|
||||
/// </value>
|
||||
public templates: LGTemplate[];
|
||||
export class Templates implements Iterable<Template> {
|
||||
private items: Template[];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets import elements that this LG file contains directly.
|
||||
|
@ -38,7 +31,7 @@ export class LGFile {
|
|||
/// <value>
|
||||
/// import elements that this LG file contains directly.
|
||||
/// </value>
|
||||
public imports: LGImport[];
|
||||
public imports: TemplateImport[];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets diagnostics.
|
||||
|
@ -57,7 +50,7 @@ export class LGFile {
|
|||
/// <value>
|
||||
/// all references that this LG file has from Imports
|
||||
/// </value>
|
||||
public references: LGFile[];
|
||||
public references: Templates[];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets LG content.
|
||||
|
@ -99,16 +92,16 @@ export class LGFile {
|
|||
/// </value>
|
||||
public options: string[];
|
||||
|
||||
public constructor(templates?: LGTemplate[],
|
||||
imports?: LGImport[],
|
||||
public constructor(items?: Template[],
|
||||
imports?: TemplateImport[],
|
||||
diagnostics?: Diagnostic[],
|
||||
references?: LGFile[],
|
||||
references?: Templates[],
|
||||
content?: string,
|
||||
id?: string,
|
||||
expressionParser?: ExpressionParser,
|
||||
importResolverDelegate?: ImportResolverDelegate,
|
||||
options?: string[]) {
|
||||
this.templates = templates || [];
|
||||
this.items = items || [];
|
||||
this.imports = imports || [];
|
||||
this.diagnostics = diagnostics || [];
|
||||
this.references = references || [];
|
||||
|
@ -119,6 +112,37 @@ export class LGFile {
|
|||
this.options = options || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new iterator for the template collection.
|
||||
*/
|
||||
public [Symbol.iterator](): Iterator<Template> {
|
||||
let index = 0;
|
||||
return {
|
||||
next: (): IteratorResult<Template> => {
|
||||
if (index < this.items.length) {
|
||||
return { done: false, value: this.items[index++] };
|
||||
} else {
|
||||
return { done: true, value: undefined };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the internal list of collection templates.
|
||||
*/
|
||||
public toArray(): Template[] {
|
||||
return this.items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends 1 or more templates to the collection.
|
||||
* @param args List of templates to add.
|
||||
*/
|
||||
public push(...args: Template[]): void {
|
||||
args.forEach(t => this.items.push(t));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether lG parser/checker/evaluate strict mode.
|
||||
/// If strict mode is on, expression would throw exception instead of return
|
||||
|
@ -139,9 +163,9 @@ export class LGFile {
|
|||
/// <value>
|
||||
/// All templates from current lg file and reference lg files.
|
||||
/// </value>
|
||||
public get allTemplates(): LGTemplate[] {
|
||||
let result = this.templates;
|
||||
this.references.forEach((ref): LGTemplate[] => result = result.concat(ref.templates));
|
||||
public get allTemplates(): Template[] {
|
||||
let result = this.items;
|
||||
this.references.forEach((ref): Template[] => result = result.concat(ref.items));
|
||||
return Array.from(new Set(result));
|
||||
}
|
||||
|
||||
|
@ -157,13 +181,37 @@ export class LGFile {
|
|||
return Array.from(new Set(result));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* parse a file and return LG file.
|
||||
* @param filePath LG absolute file path..
|
||||
* @param importResolver resolver to resolve LG import id to template text.
|
||||
* @param expressionParser Expression parser for evaluating expressions.
|
||||
* @returns new lg file.
|
||||
*/
|
||||
public static parseFile(filePath: string, importResolver?: ImportResolverDelegate, expressionParser?: ExpressionParser): Templates {
|
||||
return TemplateParser.parseFile(filePath, importResolver, expressionParser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parser to turn lg content into a Templates.
|
||||
* @param content text content contains lg templates.
|
||||
* @param id id is the identifier of content. If importResolver is undefined, id must be a full path string.
|
||||
* @param importResolver resolver to resolve LG import id to template text.
|
||||
* @param expressionParser Expression parser for evaluating expressions.
|
||||
* @returns entity.
|
||||
*/
|
||||
public static parseText(content: string, id: string = '', importResolver?: ImportResolverDelegate, expressionParser?: ExpressionParser): Templates {
|
||||
return TemplateParser.parseText(content, id, importResolver, expressionParser);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluate a template with given name and scope.
|
||||
/// </summary>
|
||||
/// <param name="templateName">Template name to be evaluated.</param>
|
||||
/// <param name="scope">The state visible in the evaluation.</param>
|
||||
/// <returns>Evaluate result.</returns>
|
||||
public evaluateTemplate(templateName: string, scope?: object): any {
|
||||
public evaluate(templateName: string, scope?: object): any {
|
||||
this.checkErrors();
|
||||
|
||||
const evaluator = new Evaluator(this.allTemplates, this.expressionParser, this.strictMode);
|
||||
|
@ -203,7 +251,7 @@ export class LGFile {
|
|||
/// <param name="inlineStr">inline string which will be evaluated.</param>
|
||||
/// <param name="scope">scope object or JToken.</param>
|
||||
/// <returns>Evaluate result.</returns>
|
||||
public evaluate(inlineStr: string, scope?: object): any
|
||||
public evaluateText(inlineStr: string, scope?: object): any
|
||||
{
|
||||
if (inlineStr === undefined)
|
||||
{
|
||||
|
@ -213,7 +261,7 @@ export class LGFile {
|
|||
this.checkErrors();
|
||||
|
||||
// wrap inline string with "# name and -" to align the evaluation process
|
||||
const fakeTemplateId = LGExtensions.newGuid();
|
||||
const fakeTemplateId = TemplateExtensions.newGuid();
|
||||
const multiLineMark = '```';
|
||||
|
||||
inlineStr = !(inlineStr.trim().startsWith(multiLineMark) && inlineStr.includes('\n'))
|
||||
|
@ -221,8 +269,8 @@ export class LGFile {
|
|||
|
||||
const newContent = `#${ fakeTemplateId } \r\n - ${ inlineStr }`;
|
||||
|
||||
const newLgFile = LGParser.parseTextWithRef(newContent, this);
|
||||
return newLgFile.evaluateTemplate(fakeTemplateId, scope);
|
||||
const newTemplates = TemplateParser.parseTextWithRef(newContent, this);
|
||||
return newTemplates.evaluate(fakeTemplateId, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -232,8 +280,8 @@ export class LGFile {
|
|||
* @param templateBody new template body.
|
||||
* @returns new lg file.
|
||||
*/
|
||||
public updateTemplate(templateName: string, newTemplateName: string, parameters: string[], templateBody: string): LGFile {
|
||||
const template: LGTemplate = this.templates.find((u: LGTemplate): boolean => u.name === templateName);
|
||||
public updateTemplate(templateName: string, newTemplateName: string, parameters: string[], templateBody: string): Templates {
|
||||
const template: Template = this.items.find((u: Template): boolean => u.name === templateName);
|
||||
if (template === undefined) {
|
||||
return this;
|
||||
}
|
||||
|
@ -247,7 +295,7 @@ export class LGFile {
|
|||
|
||||
({startLine, stopLine} = template.getTemplateRange());
|
||||
const newContent: string = this.replaceRangeContent(this.content, startLine, stopLine, content);
|
||||
this.initialize(LGParser.parseText(newContent, this.id, this.importResolver));
|
||||
this.initialize(TemplateParser.parseText(newContent, this.id, this.importResolver));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -259,16 +307,16 @@ export class LGFile {
|
|||
* @param templateBody new template body.
|
||||
* @returns new lg file.
|
||||
*/
|
||||
public addTemplate(templateName: string, parameters: string[], templateBody: string): LGFile {
|
||||
const template: LGTemplate = this.templates.find((u: LGTemplate): boolean => u.name === templateName);
|
||||
public addTemplate(templateName: string, parameters: string[], templateBody: string): Templates {
|
||||
const template: Template = this.items.find((u: Template): boolean => u.name === templateName);
|
||||
if (template !== undefined) {
|
||||
throw new Error(LGErrors.templateExist(templateName));
|
||||
throw new Error(TemplateErrors.templateExist(templateName));
|
||||
}
|
||||
|
||||
const templateNameLine: string = this.buildTemplateNameLine(templateName, parameters);
|
||||
const newTemplateBody: string = this.convertTemplateBody(templateBody);
|
||||
const newContent = `${ this.content.trimRight() }\r\n\r\n${ templateNameLine }\r\n${ newTemplateBody }\r\n`;
|
||||
this.initialize(LGParser.parseText(newContent, this.id, this.importResolver));
|
||||
this.initialize(TemplateParser.parseText(newContent, this.id, this.importResolver));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -278,8 +326,8 @@ export class LGFile {
|
|||
* @param templateName which template should delete.
|
||||
* @returns return the new lg file.
|
||||
*/
|
||||
public deleteTemplate(templateName: string): LGFile {
|
||||
const template: LGTemplate = this.templates.find((u: LGTemplate): boolean => u.name === templateName);
|
||||
public deleteTemplate(templateName: string): Templates {
|
||||
const template: Template = this.items.find((u: Template): boolean => u.name === templateName);
|
||||
if (template === undefined) {
|
||||
return this;
|
||||
}
|
||||
|
@ -290,7 +338,7 @@ export class LGFile {
|
|||
({startLine, stopLine} = template.getTemplateRange());
|
||||
|
||||
const newContent: string = this.replaceRangeContent(this.content, startLine, stopLine, undefined);
|
||||
this.initialize(LGParser.parseText(newContent, this.id, this.importResolver));
|
||||
this.initialize(TemplateParser.parseText(newContent, this.id, this.importResolver));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -402,16 +450,16 @@ export class LGFile {
|
|||
}
|
||||
}
|
||||
|
||||
private initialize(lgfile: LGFile): void {
|
||||
this.templates = lgfile.templates;
|
||||
this.imports = lgfile.imports;
|
||||
this.diagnostics = lgfile.diagnostics;
|
||||
this.references = lgfile.references;
|
||||
this.content = lgfile.content;
|
||||
this.importResolver = lgfile.importResolver;
|
||||
this.id = lgfile.id;
|
||||
this.expressionParser = lgfile.expressionParser;
|
||||
this.options = lgfile.options;
|
||||
private initialize(templates: Templates): void {
|
||||
this.items = templates.items;
|
||||
this.imports = templates.imports;
|
||||
this.diagnostics = templates.diagnostics;
|
||||
this.references = templates.references;
|
||||
this.content = templates.content;
|
||||
this.importResolver = templates.importResolver;
|
||||
this.id = templates.id;
|
||||
this.expressionParser = templates.expressionParser;
|
||||
this.options = templates.options;
|
||||
}
|
||||
|
||||
private checkErrors(): void {
|
|
@ -1,60 +0,0 @@
|
|||
const { LGParser, ActivityChecker } = require('../lib');
|
||||
const assert = require('assert');
|
||||
|
||||
function getTemplateEngine(){
|
||||
const filePath = `${ __dirname }/testData/examples/DiagnosticStructuredLG.lg`;
|
||||
return LGParser.parseFile(filePath);
|
||||
}
|
||||
|
||||
function getDiagnostics(templateName, data){
|
||||
const lgfile = getTemplateEngine();
|
||||
const lgResult = lgfile.evaluateTemplate(templateName, data);
|
||||
return ActivityChecker.check(lgResult);
|
||||
}
|
||||
|
||||
describe('ActivityCheckerTest', function() {
|
||||
it('inlineActivityChecker', function() {
|
||||
const diagnostics = ActivityChecker.check('Not a valid json');
|
||||
assert(diagnostics.length === 1);
|
||||
assert.strictEqual(diagnostics[0].severity, 1);
|
||||
assert.strictEqual(diagnostics[0].message, 'LG output is not a json object, and will fallback to string format.');
|
||||
});
|
||||
|
||||
it('emptyActivityChecker', function() {
|
||||
const diagnostics = ActivityChecker.check('{}');
|
||||
assert(diagnostics.length === 1);
|
||||
assert.strictEqual(diagnostics[0].severity, 0);
|
||||
assert.strictEqual(diagnostics[0].message, `'lgType' does not exist in lg output json object.`);
|
||||
});
|
||||
|
||||
it('ErrorStructuredType', function() {
|
||||
const diagnostics = getDiagnostics('ErrorStructuredType', undefined);
|
||||
assert(diagnostics.length === 1);
|
||||
assert.strictEqual(diagnostics[0].severity, 0);
|
||||
assert.strictEqual(diagnostics[0].message, `Type 'mystruct' is not supported currently.`);
|
||||
});
|
||||
|
||||
it('ErrorActivityType', function() {
|
||||
const diagnostics = getDiagnostics('ErrorActivityType', undefined);
|
||||
assert(diagnostics.length === 2);
|
||||
assert.strictEqual(diagnostics[0].severity, 0);
|
||||
assert.strictEqual(diagnostics[0].message, `'xxx' is not a valid activity type.`);
|
||||
assert.strictEqual(diagnostics[1].severity, 1);
|
||||
assert.strictEqual(diagnostics[1].message, `'invalidproperty' not support in Activity.`);
|
||||
});
|
||||
|
||||
it('ErrorMessage', function() {
|
||||
const diagnostics = getDiagnostics('ErrorMessage', undefined);
|
||||
assert(diagnostics.length === 5);
|
||||
assert.strictEqual(diagnostics[0].severity, 1);
|
||||
assert.strictEqual(diagnostics[0].message, `'attachment,suggestedaction' not support in Activity.`);
|
||||
assert.strictEqual(diagnostics[1].severity, 1);
|
||||
assert.strictEqual(diagnostics[1].message, `'mystruct' is not card action type.`);
|
||||
assert.strictEqual(diagnostics[2].severity, 0);
|
||||
assert.strictEqual(diagnostics[2].message, `'yyy' is not a valid card action type.`);
|
||||
assert.strictEqual(diagnostics[3].severity, 0);
|
||||
assert.strictEqual(diagnostics[3].message, `'notsure' is not a boolean value.`);
|
||||
assert.strictEqual(diagnostics[4].severity, 1);
|
||||
assert.strictEqual(diagnostics[4].message, `'mystruct' is not an attachment type.`);
|
||||
});
|
||||
});
|
|
@ -1,23 +1,49 @@
|
|||
const { LGParser, ActivityFactory } = require('../lib');
|
||||
const { Templates } = require('../lib');
|
||||
const {ActivityFactory} = require('botbuilder-core')
|
||||
const assert = require('assert');
|
||||
|
||||
function getTemplateEngine(){
|
||||
function getTemplates(){
|
||||
const filePath = `${ __dirname }/testData/examples/NormalStructuredLG.lg`;
|
||||
return LGParser.parseFile(filePath);
|
||||
return Templates.parseFile(filePath);
|
||||
}
|
||||
|
||||
function getActivity(templateName, data){
|
||||
const lgFile = getTemplateEngine();
|
||||
const lgResult = lgFile.evaluateTemplate(templateName, data);
|
||||
return ActivityFactory.createActivity(lgResult);
|
||||
const templates = getTemplates();
|
||||
const lgResult = templates.evaluate(templateName, data);
|
||||
return ActivityFactory.fromObject(lgResult);
|
||||
}
|
||||
|
||||
function getDiagnostics(templateName, data){
|
||||
const filePath = `${ __dirname }/testData/examples/DiagnosticStructuredLG.lg`;
|
||||
const templates = Templates.parseFile(filePath);
|
||||
const lgResult = templates.evaluate(templateName, data);
|
||||
return ActivityFactory.checkLGResult(lgResult) ;
|
||||
}
|
||||
|
||||
|
||||
describe('ActivityFactoryTest', function() {
|
||||
|
||||
it('inlineActivityFactory', function() {
|
||||
let result = ActivityFactory.createActivity('text');
|
||||
let result = ActivityFactory.fromObject('text');
|
||||
assert(result.text === 'text');
|
||||
assert(result.speak === 'text');
|
||||
assert(result.inputHint === undefined);
|
||||
|
||||
const data = {
|
||||
title: 'titleContent',
|
||||
text: 'textContent'
|
||||
};
|
||||
|
||||
let cnt = 0;
|
||||
const tmpl = getTemplates();
|
||||
for (let t of tmpl) {
|
||||
cnt++;
|
||||
}
|
||||
|
||||
const cardActionLgResult = tmpl.evaluateText('${HerocardWithCardAction()}', data);
|
||||
result = ActivityFactory.fromObject(cardActionLgResult);
|
||||
assertCardActionActivity(result);
|
||||
|
||||
});
|
||||
|
||||
it('NotSupportStructuredType', function() {
|
||||
|
@ -235,6 +261,35 @@ describe('ActivityFactoryTest', function() {
|
|||
let result = getActivity('SuggestedActionsReference', data);
|
||||
assertSuggestedActionsReferenceActivity(result);
|
||||
});
|
||||
|
||||
it('CheckOutPutNotFromStructuredLG', function() {
|
||||
let diagnostics = ActivityFactory.checkLGResult('Not a valid json');
|
||||
assert(diagnostics.length === 1);
|
||||
assert.strictEqual(diagnostics[0], '[WARNING]LG output is not a json object, and will fallback to string format.');
|
||||
|
||||
diagnostics = ActivityFactory.checkLGResult('{}');
|
||||
assert(diagnostics.length === 1);
|
||||
assert.strictEqual(diagnostics[0], `[ERROR]'lgType' does not exist in lg output json object.`);
|
||||
});
|
||||
|
||||
it('CheckStructuredLGDiagnostics', function() {
|
||||
let diagnostics = getDiagnostics('ErrorStructuredType', undefined);
|
||||
assert(diagnostics.length === 1);
|
||||
assert.strictEqual(diagnostics[0], `[ERROR]Type 'mystruct' is not supported currently.`);
|
||||
|
||||
diagnostics = getDiagnostics('ErrorActivityType', undefined);
|
||||
assert(diagnostics.length === 2);
|
||||
assert.strictEqual(diagnostics[0], `[ERROR]'xxx' is not a valid activity type.`);
|
||||
assert.strictEqual(diagnostics[1], `[WARNING]'invalidproperty' not support in Activity.`);
|
||||
|
||||
diagnostics = getDiagnostics('ErrorMessage', undefined);
|
||||
assert(diagnostics.length === 5);
|
||||
assert.strictEqual(diagnostics[0], `[WARNING]'attachment,suggestedaction' not support in Activity.`);
|
||||
assert.strictEqual(diagnostics[1], `[WARNING]'mystruct' is not card action type.`);
|
||||
assert.strictEqual(diagnostics[2], `[ERROR]'yyy' is not a valid card action type.`);
|
||||
assert.strictEqual(diagnostics[3], `[ERROR]'notsure' is not a boolean value.`);
|
||||
assert.strictEqual(diagnostics[4], `[WARNING]'mystruct' is not an attachment type.`);
|
||||
});
|
||||
});
|
||||
|
||||
function assertSuggestedActionsReferenceActivity(activity) {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
const { MultiLanguageLG } = require(`../`);
|
||||
const assert = require(`assert`);
|
||||
|
||||
describe('MultilanguageLGTest', function() {
|
||||
it('TestMultiLanguageLG', function() {
|
||||
const localPerFile = new Map();
|
||||
localPerFile.set('en', `${ __dirname }/testData/MultiLanguage/a.en.lg`);
|
||||
localPerFile.set('', `${ __dirname }/testData/MultiLanguage/a.lg`);
|
||||
|
||||
const generator = new MultiLanguageLG(localPerFile);
|
||||
|
||||
// fallback to "a.en.lg"
|
||||
let result = generator.generate('${templatec()}', undefined, 'en-us');
|
||||
assert.strictEqual(result, 'from a.en.lg');
|
||||
|
||||
// "a.en.lg" is used
|
||||
result = generator.generate('${templatec()}', undefined, 'en');
|
||||
assert.strictEqual(result, 'from a.en.lg');
|
||||
|
||||
// locale "fr" has no entry file, default file "a.lg" is used
|
||||
result = generator.generate('${templatec()}', undefined, 'fr');
|
||||
assert.strictEqual(result, 'from a.lg');
|
||||
|
||||
// "a.lg" is used
|
||||
result = generator.generate('${templatec()}');
|
||||
assert.strictEqual(result, 'from a.lg');
|
||||
});
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
const { LGParser } = require('../');
|
||||
const { Templates } = require('../');
|
||||
const { SimpleObjectMemory, ExpressionParser, ExpressionFunctions, Expression } = require('adaptive-expressions');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
@ -9,89 +9,100 @@ function GetExampleFilePath(fileName) {
|
|||
|
||||
|
||||
describe('LG', function() {
|
||||
it('TestEnumeration', function () {
|
||||
let cnt = 0;
|
||||
let templates = Templates.parseFile(GetExampleFilePath('2.lg'));
|
||||
for (let t of templates) {
|
||||
assert.strictEqual(typeof t, 'object');
|
||||
assert.strictEqual(t.name, 'wPhrase');
|
||||
cnt++;
|
||||
}
|
||||
assert.strictEqual(cnt, 1);
|
||||
});
|
||||
|
||||
it('TestBasic', function() {
|
||||
let LGFile = LGParser.parseFile(GetExampleFilePath('2.lg'));
|
||||
let evaled = LGFile.evaluateTemplate('wPhrase');
|
||||
let templates = Templates.parseFile(GetExampleFilePath('2.lg'));
|
||||
let evaled = templates.evaluate('wPhrase');
|
||||
const options = ['Hi', 'Hello', 'Hiya'];
|
||||
assert.strictEqual(options.includes(evaled), true, `The result ${ evaled } is not in those options [${ options.join(',') }]`);
|
||||
});
|
||||
|
||||
it('TestBasicTemplateReference', function() {
|
||||
let LGFile = LGParser.parseFile(GetExampleFilePath('3.lg'));
|
||||
console.log(LGFile.templates[0].body)
|
||||
console.log(LGFile.templates[1].body)
|
||||
let evaled = LGFile.evaluateTemplate('welcome-user', undefined);
|
||||
let templates = Templates.parseFile(GetExampleFilePath('3.lg'));
|
||||
console.log(templates.toArray()[0].body);
|
||||
console.log(templates.toArray()[1].body);
|
||||
let evaled = templates.evaluate('welcome-user', undefined);
|
||||
const options = ['Hi', 'Hello', 'Hiya', 'Hi :)', 'Hello :)', 'Hiya :)'];
|
||||
assert.strictEqual(options.includes(evaled), true, `The result ${ evaled } is not in those options [${ options.join(',') }]`);
|
||||
});
|
||||
|
||||
it('TestBasicTemplateRefAndEntityRef', function() {
|
||||
let LGFile = LGParser.parseFile(GetExampleFilePath('4.lg'));
|
||||
let templates = Templates.parseFile(GetExampleFilePath('4.lg'));
|
||||
let userName = 'DL';
|
||||
let evaled = LGFile.evaluateTemplate('welcome-user', { userName: userName });
|
||||
let evaled = templates.evaluate('welcome-user', { userName: userName });
|
||||
const options = ['Hi', 'Hello', 'Hiya ', 'Hi :)', 'Hello :)', 'Hiya :)'];
|
||||
assert.strictEqual(evaled.includes(userName), true, `The result ${ evaled } does not contiain ${ userName }`);
|
||||
});
|
||||
|
||||
it('TestBasicConditionalTemplate', function() {
|
||||
let LGFile = LGParser.parseFile(GetExampleFilePath('5.lg'));
|
||||
let templates = Templates.parseFile(GetExampleFilePath('5.lg'));
|
||||
|
||||
let evaled = LGFile.evaluateTemplate('time-of-day-readout', { timeOfDay: 'morning' });
|
||||
let evaled = templates.evaluate('time-of-day-readout', { timeOfDay: 'morning' });
|
||||
assert.strictEqual(evaled === 'Good morning' || evaled === 'Morning! ', true, `Evaled is ${ evaled }`);
|
||||
|
||||
evaled = LGFile.evaluateTemplate('time-of-day-readout', { timeOfDay: 'evening' });
|
||||
evaled = templates.evaluate('time-of-day-readout', { timeOfDay: 'evening' });
|
||||
assert.strictEqual(evaled === 'Good evening' || evaled === 'Evening! ', true, `Evaled is ${ evaled }`);
|
||||
});
|
||||
|
||||
it('TestBasicConditionalTemplateWithoutDefault', function() {
|
||||
let LGFile = LGParser.parseFile(GetExampleFilePath('5.lg'));
|
||||
let templates = Templates.parseFile(GetExampleFilePath('5.lg'));
|
||||
|
||||
let evaled = LGFile.evaluateTemplate('time-of-day-readout-without-default', { timeOfDay: 'morning' });
|
||||
let evaled = templates.evaluate('time-of-day-readout-without-default', { timeOfDay: 'morning' });
|
||||
assert.strictEqual(evaled === 'Good morning' || evaled === 'Morning! ', true, `Evaled is ${ evaled }`);
|
||||
|
||||
evaled = LGFile.evaluateTemplate('time-of-day-readout-without-default2', { timeOfDay: 'morning' });
|
||||
evaled = templates.evaluate('time-of-day-readout-without-default2', { timeOfDay: 'morning' });
|
||||
assert.strictEqual(evaled === 'Good morning' || evaled === 'Morning! ', true, `Evaled is ${ evaled }`);
|
||||
|
||||
evaled = LGFile.evaluateTemplate('time-of-day-readout-without-default2', { timeOfDay: 'evening' });
|
||||
evaled = templates.evaluate('time-of-day-readout-without-default2', { timeOfDay: 'evening' });
|
||||
assert.strictEqual(evaled, undefined, `Evaled is ${ evaled } which should be undefined.`);
|
||||
});
|
||||
|
||||
it('TestBasicTemplateRefWithParameters', function() {
|
||||
let LGFile = LGParser.parseFile(GetExampleFilePath('6.lg'));
|
||||
let templates = Templates.parseFile(GetExampleFilePath('6.lg'));
|
||||
|
||||
let evaled = LGFile.evaluateTemplate('welcome', undefined);
|
||||
let evaled = templates.evaluate('welcome', undefined);
|
||||
const options1 = ['Hi DongLei :)', 'Hey DongLei :)', 'Hello DongLei :)'];
|
||||
assert.strictEqual(options1.includes(evaled), true, `Evaled is ${ evaled }`);
|
||||
|
||||
const options2 = ['Hi DL :)', 'Hey DL :)', 'Hello DL :)'];
|
||||
evaled = LGFile.evaluateTemplate('welcome', { userName: 'DL' });
|
||||
evaled = templates.evaluate('welcome', { userName: 'DL' });
|
||||
assert.strictEqual(options2.includes(evaled), true, `Evaled is ${ evaled }`);
|
||||
});
|
||||
|
||||
it('TestBasicSwitchCaseTemplate', function() {
|
||||
let LGFile = LGParser.parseFile(GetExampleFilePath('switchcase.lg'));
|
||||
let evaled1 = LGFile.evaluateTemplate('greetInAWeek', { day: 'Saturday' });
|
||||
let templates = Templates.parseFile(GetExampleFilePath('switchcase.lg'));
|
||||
let evaled1 = templates.evaluate('greetInAWeek', { day: 'Saturday' });
|
||||
assert.strictEqual(evaled1 === 'Happy Saturday!', true, `Evaled is ${ evaled1 }`);
|
||||
|
||||
let evaled3 = LGFile.evaluateTemplate('greetInAWeek', { day: 'Monday' });
|
||||
let evaled3 = templates.evaluate('greetInAWeek', { day: 'Monday' });
|
||||
assert.strictEqual(evaled3 === 'Work Hard!', true, `Evaled is ${ evaled3 }`);
|
||||
});
|
||||
|
||||
it('TestBasicListSupport', function() {
|
||||
let LGFile = LGParser.parseFile(GetExampleFilePath('BasicList.lg'));
|
||||
let templates = Templates.parseFile(GetExampleFilePath('BasicList.lg'));
|
||||
|
||||
let evaled = LGFile.evaluateTemplate('BasicJoin', { items: ['1'] });
|
||||
let evaled = templates.evaluate('BasicJoin', { items: ['1'] });
|
||||
assert.strictEqual(evaled, '1', `Evaled is ${ evaled }`);
|
||||
|
||||
evaled = LGFile.evaluateTemplate('BasicJoin', { items: ['1', '2'] });
|
||||
evaled = templates.evaluate('BasicJoin', { items: ['1', '2'] });
|
||||
assert.strictEqual(evaled, '1, 2', `Evaled is ${ evaled }`);
|
||||
|
||||
evaled = LGFile.evaluateTemplate('BasicJoin', { items: ['1', '2', '3'] });
|
||||
evaled = templates.evaluate('BasicJoin', { items: ['1', '2', '3'] });
|
||||
assert.strictEqual(evaled, '1, 2 and 3', `Evaled is ${ evaled }`);
|
||||
});
|
||||
|
||||
it('TestBasicExtendedFunctions', function() {
|
||||
let LGFile = LGParser.parseFile(GetExampleFilePath('6.lg'));
|
||||
let templates = Templates.parseFile(GetExampleFilePath('6.lg'));
|
||||
const alarms = [
|
||||
{
|
||||
time: '7 am',
|
||||
|
@ -103,18 +114,18 @@ describe('LG', function() {
|
|||
}
|
||||
];
|
||||
|
||||
let evaled = LGFile.evaluateTemplate('ShowAlarmsWithForeach', { alarms: alarms });
|
||||
let evaled = templates.evaluate('ShowAlarmsWithForeach', { alarms: alarms });
|
||||
assert.strictEqual(evaled === 'You have 2 alarms, 7 am at tomorrow and 8 pm at tomorrow', true, `Evaled is ${ evaled }`);
|
||||
|
||||
evaled = LGFile.evaluateTemplate('ShowAlarmsWithLgTemplate', { alarms: alarms });
|
||||
evaled = templates.evaluate('ShowAlarmsWithLgTemplate', { alarms: alarms });
|
||||
assert.strictEqual(evaled === 'You have 2 alarms, 7 am at tomorrow and 8 pm at tomorrow', true, `Evaled is ${ evaled }`);
|
||||
|
||||
evaled = LGFile.evaluateTemplate('ShowAlarmsWithDynamicLgTemplate', { alarms: alarms, templateName: 'ShowAlarm' });
|
||||
evaled = templates.evaluate('ShowAlarmsWithDynamicLgTemplate', { alarms: alarms, templateName: 'ShowAlarm' });
|
||||
assert.strictEqual(evaled === 'You have 2 alarms, 7 am at tomorrow and 8 pm at tomorrow', true, `Evaled is ${ evaled }`);
|
||||
});
|
||||
|
||||
it('TestCaseInsensitive', function() {
|
||||
let LGFile = LGParser.parseFile(GetExampleFilePath('CaseInsensitive.lg'));
|
||||
let templates = Templates.parseFile(GetExampleFilePath('CaseInsensitive.lg'));
|
||||
const alarms = [
|
||||
{
|
||||
time: '7 am',
|
||||
|
@ -126,83 +137,83 @@ describe('LG', function() {
|
|||
}
|
||||
];
|
||||
|
||||
let evaled = LGFile.evaluateTemplate('ShowAlarms', { alarms: alarms });
|
||||
let evaled = templates.evaluate('ShowAlarms', { alarms: alarms });
|
||||
assert.strictEqual(evaled === 'You have two alarms', true, `Evaled is ${ evaled }`);
|
||||
|
||||
let evaled1 = LGFile.evaluateTemplate('greetInAWeek', { day: 'Saturday' });
|
||||
let evaled1 = templates.evaluate('greetInAWeek', { day: 'Saturday' });
|
||||
assert.strictEqual(evaled1 === 'Happy Saturday!', true, `Evaled is ${ evaled1 }`);
|
||||
});
|
||||
|
||||
it('TestListWithOnlyOneElement', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('8.lg'));
|
||||
var evaled = LGFile.evaluateTemplate('ShowTasks', { recentTasks: ['Task1'] });
|
||||
var templates = Templates.parseFile(GetExampleFilePath('8.lg'));
|
||||
var evaled = templates.evaluate('ShowTasks', { recentTasks: ['Task1'] });
|
||||
assert.strictEqual(evaled === 'Your most recent task is Task1. You can let me know if you want to add or complete a task.', true, `Evaled is ${ evaled }`);
|
||||
});
|
||||
|
||||
it('TestTemplateNameWithDotIn', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('TemplateNameWithDot.lg'));
|
||||
var evaled1 = LGFile.evaluateTemplate('Hello.World', '');
|
||||
var templates = Templates.parseFile(GetExampleFilePath('TemplateNameWithDot.lg'));
|
||||
var evaled1 = templates.evaluate('Hello.World', '');
|
||||
assert.strictEqual(evaled1 === 'Hello World', true, `Evaled is ${ evaled1 }`);
|
||||
|
||||
var evaled2 = LGFile.evaluateTemplate('Hello', '');
|
||||
var evaled2 = templates.evaluate('Hello', '');
|
||||
assert.strictEqual(evaled2 === 'Hello World', true, `Evaled is ${ evaled2 }`);
|
||||
});
|
||||
|
||||
it('TestMultiLine', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('MultilineTextForAdaptiveCard.lg'));
|
||||
var evaled1 = LGFile.evaluateTemplate('wPhrase', '');
|
||||
var templates = Templates.parseFile(GetExampleFilePath('MultilineTextForAdaptiveCard.lg'));
|
||||
var evaled1 = templates.evaluate('wPhrase', '');
|
||||
var options1 = ['\r\ncardContent\r\n', 'hello', '\ncardContent\n'];
|
||||
assert.strictEqual(options1.includes(evaled1), true, `1.Evaled is ${ evaled1 }`);
|
||||
|
||||
var evaled2 = LGFile.evaluateTemplate('nameTemplate', { name: 'N' });
|
||||
var evaled2 = templates.evaluate('nameTemplate', { name: 'N' });
|
||||
var options2 = ['\r\nN\r\n', 'N', '\nN\n'];
|
||||
assert.strictEqual(options2.includes(evaled2), true, `2.Evaled is ${ evaled2 }`);
|
||||
|
||||
var evaled3 = LGFile.evaluateTemplate('adaptivecardsTemplate', '');
|
||||
var evaled3 = templates.evaluate('adaptivecardsTemplate', '');
|
||||
console.log(evaled3);
|
||||
|
||||
var evaled4 = LGFile.evaluateTemplate('refTemplate', '');
|
||||
var evaled4 = templates.evaluate('refTemplate', '');
|
||||
var options4 = ['\r\nhi\r\n', '\nhi\n'];
|
||||
assert.strictEqual(options4.includes(evaled4), true, `4.Evaled is ${ evaled4 }`);
|
||||
});
|
||||
|
||||
it('TestTemplateRef', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('TemplateRef.lg'));
|
||||
var templates = Templates.parseFile(GetExampleFilePath('TemplateRef.lg'));
|
||||
var scope = { time: 'morning', name: 'Dong Lei' };
|
||||
var evaled1 = LGFile.evaluateTemplate('Hello', scope);
|
||||
var evaled1 = templates.evaluate('Hello', scope);
|
||||
assert.strictEqual(evaled1, 'Good morning Dong Lei', `Evaled is ${ evaled1 }`);
|
||||
});
|
||||
|
||||
it('TestEscapeCharacter', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('EscapeCharacter.lg'));
|
||||
var evaled = LGFile.evaluateTemplate('wPhrase', undefined);
|
||||
var templates = Templates.parseFile(GetExampleFilePath('EscapeCharacter.lg'));
|
||||
var evaled = templates.evaluate('wPhrase', undefined);
|
||||
assert.strictEqual(evaled, 'Hi \r\n\t[]{}\\', 'Happy path failed.');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('otherEscape', undefined);
|
||||
evaled = templates.evaluate('otherEscape', undefined);
|
||||
assert.strictEqual(evaled, 'Hi y ', 'Happy path failed.');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('escapeInExpression', undefined);
|
||||
evaled = templates.evaluate('escapeInExpression', undefined);
|
||||
assert.strictEqual(evaled, 'Hi hello\\\\');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('escapeInExpression2', undefined);
|
||||
evaled = templates.evaluate('escapeInExpression2', undefined);
|
||||
assert.strictEqual(evaled, 'Hi hello\'');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('escapeInExpression3', undefined);
|
||||
evaled = templates.evaluate('escapeInExpression3', undefined);
|
||||
assert.strictEqual(evaled, 'Hi hello"');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('escapeInExpression4', undefined);
|
||||
evaled = templates.evaluate('escapeInExpression4', undefined);
|
||||
assert.strictEqual(evaled, 'Hi hello"');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('escapeInExpression5', undefined);
|
||||
evaled = templates.evaluate('escapeInExpression5', undefined);
|
||||
assert.strictEqual(evaled, 'Hi hello\n');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('escapeInExpression6', undefined);
|
||||
evaled = templates.evaluate('escapeInExpression6', undefined);
|
||||
assert.strictEqual(evaled, 'Hi hello\n');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('showTodo', { todos: ['A', 'B', 'C'] });
|
||||
evaled = templates.evaluate('showTodo', { todos: ['A', 'B', 'C'] });
|
||||
assert.strictEqual(evaled.replace(/\r\n/g, '\n'), '\n Your most recent 3 tasks are\n * A\n* B\n* C\n ');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('showTodo', undefined);
|
||||
evaled = templates.evaluate('showTodo', undefined);
|
||||
assert.strictEqual(evaled.replace(/\r\n/g, '\n'), '\n You don\'t have any "t\\\\odo\'".\n ');
|
||||
});
|
||||
|
||||
|
@ -237,8 +248,8 @@ describe('LG', function() {
|
|||
}
|
||||
];
|
||||
for (const testItem of testData) {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('Analyzer.lg'));
|
||||
var evaled1 = LGFile.analyzeTemplate(testItem.name);
|
||||
var templates = Templates.parseFile(GetExampleFilePath('Analyzer.lg'));
|
||||
var evaled1 = templates.analyzeTemplate(testItem.name);
|
||||
var variableEvaled = evaled1.Variables;
|
||||
var variableEvaledOptions = testItem.variableOptions;
|
||||
assert.strictEqual(variableEvaled.length, variableEvaledOptions.length);
|
||||
|
@ -251,119 +262,119 @@ describe('LG', function() {
|
|||
});
|
||||
|
||||
it('TestlgTemplateFunction', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('lgTemplate.lg'));
|
||||
var evaled = LGFile.evaluateTemplate('TemplateC', '');
|
||||
var templates = Templates.parseFile(GetExampleFilePath('lgTemplate.lg'));
|
||||
var evaled = templates.evaluate('TemplateC', '');
|
||||
var options = ['Hi', 'Hello'];
|
||||
assert.strictEqual(options.includes(evaled), true);
|
||||
|
||||
evaled = LGFile.evaluateTemplate('TemplateD', { b: 'morning' });
|
||||
evaled = templates.evaluate('TemplateD', { b: 'morning' });
|
||||
options = ['Hi morning', 'Hello morning'];
|
||||
assert.strictEqual(options.includes(evaled), true);
|
||||
});
|
||||
|
||||
it('TestTemplateAsFunction', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('TemplateAsFunction.lg'));
|
||||
var templates = Templates.parseFile(GetExampleFilePath('TemplateAsFunction.lg'));
|
||||
|
||||
var evaled = LGFile.evaluateTemplate('Test2');
|
||||
var evaled = templates.evaluate('Test2');
|
||||
assert.strictEqual(evaled, 'hello world', `Evaled is ${ evaled }`);
|
||||
|
||||
evaled = LGFile.evaluateTemplate('Test3');
|
||||
evaled = templates.evaluate('Test3');
|
||||
assert.strictEqual(evaled, 'hello world', `Evaled is ${ evaled }`);
|
||||
|
||||
evaled = LGFile.evaluateTemplate('Test4');
|
||||
evaled = templates.evaluate('Test4');
|
||||
assert.strictEqual(evaled.trim(), 'hello world', `Evaled is ${ evaled }`);
|
||||
|
||||
evaled = LGFile.evaluateTemplate('dupNameWithTemplate');
|
||||
evaled = templates.evaluate('dupNameWithTemplate');
|
||||
assert.strictEqual(evaled, 2, `Evaled is ${ evaled }`);
|
||||
});
|
||||
|
||||
it('TestAnalyzelgTemplateFunction', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('lgTemplate.lg'));
|
||||
var evaled = LGFile.analyzeTemplate('TemplateD');
|
||||
var templates = Templates.parseFile(GetExampleFilePath('lgTemplate.lg'));
|
||||
var evaled = templates.analyzeTemplate('TemplateD');
|
||||
var variableEvaled = evaled.Variables;
|
||||
var options = ['b'];
|
||||
assert.strictEqual(variableEvaled.length, options.length);
|
||||
options.forEach(e => assert.strictEqual(variableEvaled.includes(e), true));
|
||||
});
|
||||
|
||||
it('TestImportLgFiles', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('importExamples/import.lg'));
|
||||
it('TestImporttemplatess', function() {
|
||||
var templates = Templates.parseFile(GetExampleFilePath('importExamples/import.lg'));
|
||||
|
||||
// Assert 6.lg is imported only once when there are several relative paths which point to the same file.
|
||||
// Assert import cycle loop is handled well as expected when a file imports itself.
|
||||
assert.strictEqual(LGFile.allTemplates.length, 14);
|
||||
assert.strictEqual(templates.allTemplates.length, 14);
|
||||
|
||||
const options1 = ['Hi', 'Hello', 'Hey'];
|
||||
var evaled = LGFile.evaluateTemplate('basicTemplate');
|
||||
var evaled = templates.evaluate('basicTemplate');
|
||||
assert.strictEqual(options1.includes(evaled), true, `Evaled is ${ evaled }`);
|
||||
|
||||
const options2 = ['Hi DongLei :)', 'Hey DongLei :)', 'Hello DongLei :)'];
|
||||
evaled = LGFile.evaluateTemplate('welcome');
|
||||
evaled = templates.evaluate('welcome');
|
||||
assert.strictEqual(options2.includes(evaled), true, `Evaled is ${ evaled }`);
|
||||
|
||||
const options3 = ['Hi DL :)', 'Hey DL :)', 'Hello DL :)'];
|
||||
evaled = LGFile.evaluateTemplate('welcome', { userName: 'DL' });
|
||||
evaled = templates.evaluate('welcome', { userName: 'DL' });
|
||||
assert.strictEqual(options3.includes(evaled), true, `Evaled is ${ evaled }`);
|
||||
|
||||
const options4 = ['Hi 2', 'Hello 2'];
|
||||
evaled = LGFile.evaluateTemplate('basicTemplate2');
|
||||
evaled = templates.evaluate('basicTemplate2');
|
||||
assert.strictEqual(options4.includes(evaled), true, `Evaled is ${ evaled }`);
|
||||
|
||||
const options5 = ['Hi 2', 'Hello 2'];
|
||||
evaled = LGFile.evaluateTemplate('template3');
|
||||
evaled = templates.evaluate('template3');
|
||||
assert.strictEqual(options5.includes(evaled), true, `Evaled is ${ evaled }`);
|
||||
|
||||
// Assert 6.lg of relative path is imported from text.
|
||||
LGFile = LGParser.parseText(`# basicTemplate\r\n- Hi\r\n- Hello\r\n[import](./6.lg)`, GetExampleFilePath('xx.lg'));
|
||||
templates = Templates.parseText(`# basicTemplate\r\n- Hi\r\n- Hello\r\n[import](./6.lg)`, GetExampleFilePath('xx.lg'));
|
||||
|
||||
assert.strictEqual(LGFile.allTemplates.length, 8);
|
||||
assert.strictEqual(templates.allTemplates.length, 8);
|
||||
|
||||
evaled = LGFile.evaluateTemplate('basicTemplate');
|
||||
evaled = templates.evaluate('basicTemplate');
|
||||
assert.strictEqual(options1.includes(evaled), true, `Evaled is ${ evaled }`);
|
||||
|
||||
evaled = LGFile.evaluateTemplate('welcome');
|
||||
evaled = templates.evaluate('welcome');
|
||||
assert.strictEqual(options2.includes(evaled), true, `Evaled is ${ evaled }`);
|
||||
|
||||
evaled = LGFile.evaluateTemplate('welcome', { userName: 'DL' });
|
||||
evaled = templates.evaluate('welcome', { userName: 'DL' });
|
||||
assert.strictEqual(options3.includes(evaled), true, `Evaled is ${ evaled }`);
|
||||
});
|
||||
|
||||
it('TestRegex', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('Regex.lg'));
|
||||
var evaled = LGFile.evaluateTemplate('wPhrase');
|
||||
var templates = Templates.parseFile(GetExampleFilePath('Regex.lg'));
|
||||
var evaled = templates.evaluate('wPhrase');
|
||||
assert.strictEqual(evaled, 'Hi');
|
||||
|
||||
var evaled = LGFile.evaluateTemplate('wPhrase', {name: 'jack'});
|
||||
var evaled = templates.evaluate('wPhrase', {name: 'jack'});
|
||||
assert.strictEqual(evaled, 'Hi jack');
|
||||
|
||||
var evaled = LGFile.evaluateTemplate('wPhrase', {name: 'morethanfive'});
|
||||
var evaled = templates.evaluate('wPhrase', {name: 'morethanfive'});
|
||||
assert.strictEqual(evaled, 'Hi');
|
||||
});
|
||||
|
||||
it('TestExpandTemplate', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('Expand.lg'));
|
||||
var templates = Templates.parseFile(GetExampleFilePath('Expand.lg'));
|
||||
|
||||
// without scope
|
||||
var evaled = LGFile.expandTemplate('FinalGreeting');
|
||||
var evaled = templates.expandTemplate('FinalGreeting');
|
||||
assert.strictEqual(evaled.length, 4, `Evaled is ${ evaled }`);
|
||||
let expectedResults = ['Hi Morning', 'Hi Evening', 'Hello Morning', 'Hello Evening'];
|
||||
expectedResults.forEach(x => assert(evaled.includes(x)));
|
||||
|
||||
// with scope
|
||||
evaled = LGFile.expandTemplate('TimeOfDayWithCondition', { time: 'evening'});
|
||||
evaled = templates.expandTemplate('TimeOfDayWithCondition', { time: 'evening'});
|
||||
assert.strictEqual(evaled.length, 2, `Evaled is ${ evaled }`);
|
||||
expectedResults = ['Hi Evening', 'Hello Evening'];
|
||||
expectedResults.forEach(x => assert(evaled.includes(x)));
|
||||
|
||||
// with scope
|
||||
evaled = LGFile.expandTemplate('greetInAWeek', {day:'Sunday'});
|
||||
evaled = templates.expandTemplate('greetInAWeek', {day:'Sunday'});
|
||||
assert.strictEqual(evaled.length, 2, `Evaled is ${ evaled }`);
|
||||
expectedResults = ['Nice Sunday!', 'Happy Sunday!'];
|
||||
expectedResults.forEach(x => assert(evaled.includes(x)));
|
||||
});
|
||||
|
||||
it('TestExpandTemplateWithRef', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('Expand.lg'));
|
||||
var templates = Templates.parseFile(GetExampleFilePath('Expand.lg'));
|
||||
|
||||
const alarms = [
|
||||
{
|
||||
|
@ -376,14 +387,14 @@ describe('LG', function() {
|
|||
}
|
||||
];
|
||||
|
||||
var evaled = LGFile.expandTemplate('ShowAlarmsWithLgTemplate', {alarms});
|
||||
var evaled = templates.expandTemplate('ShowAlarmsWithLgTemplate', {alarms});
|
||||
assert.strictEqual(evaled.length, 2, `Evaled is ${ evaled }`);
|
||||
assert.strictEqual(evaled[0], 'You have 2 alarms, they are 8 pm at tomorrow', `Evaled is ${ evaled }`);
|
||||
assert.strictEqual(evaled[1], 'You have 2 alarms, they are 8 pm of tomorrow', `Evaled is ${ evaled }`);
|
||||
});
|
||||
|
||||
it('TestExpandTemplateWithRefInMultiLine', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('Expand.lg'));
|
||||
var templates = Templates.parseFile(GetExampleFilePath('Expand.lg'));
|
||||
|
||||
const alarms = [
|
||||
{
|
||||
|
@ -396,7 +407,7 @@ describe('LG', function() {
|
|||
}
|
||||
];
|
||||
|
||||
var evaled = LGFile.expandTemplate('ShowAlarmsWithMultiLine', {alarms});
|
||||
var evaled = templates.expandTemplate('ShowAlarmsWithMultiLine', {alarms});
|
||||
assert.strictEqual(evaled.length, 2, `Evaled is ${ evaled }`);
|
||||
const eval1Options = ['\r\nYou have 2 alarms.\r\nThey are 8 pm at tomorrow\r\n', '\nYou have 2 alarms.\nThey are 8 pm at tomorrow\n'];
|
||||
const eval2Options = ['\r\nYou have 2 alarms.\r\nThey are 8 pm of tomorrow\r\n', '\nYou have 2 alarms.\nThey are 8 pm of tomorrow\n'];
|
||||
|
@ -405,7 +416,7 @@ describe('LG', function() {
|
|||
});
|
||||
|
||||
it('TestExpandTemplateWithFunction', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('Expand.lg'));
|
||||
var templates = Templates.parseFile(GetExampleFilePath('Expand.lg'));
|
||||
|
||||
const alarms = [
|
||||
{
|
||||
|
@ -418,7 +429,7 @@ describe('LG', function() {
|
|||
}
|
||||
];
|
||||
|
||||
var evaled = LGFile.expandTemplate('ShowAlarmsWithForeach', {alarms});
|
||||
var evaled = templates.expandTemplate('ShowAlarmsWithForeach', {alarms});
|
||||
assert.strictEqual(evaled.length, 1, `Evaled is ${ evaled }`);
|
||||
const evalOptions = [
|
||||
'You have 2 alarms, 7 am at tomorrow and 8 pm at tomorrow',
|
||||
|
@ -429,31 +440,31 @@ describe('LG', function() {
|
|||
|
||||
assert(evalOptions.includes(evaled[0]));
|
||||
|
||||
evaled = LGFile.expandTemplate('T2');
|
||||
evaled = templates.expandTemplate('T2');
|
||||
assert.strictEqual(evaled.length, 1, `Evaled is ${ evaled }`);
|
||||
assert(evaled[0] === '3' || evaled[0] === '5');
|
||||
|
||||
evaled = LGFile.expandTemplate('T3');
|
||||
evaled = templates.expandTemplate('T3');
|
||||
assert.strictEqual(evaled.length, 1, `Evaled is ${ evaled }`);
|
||||
assert(evaled[0] === '3' || evaled[0] === '5');
|
||||
|
||||
evaled = LGFile.expandTemplate('T4');
|
||||
evaled = templates.expandTemplate('T4');
|
||||
assert.strictEqual(evaled.length, 1, `Evaled is ${ evaled }`);
|
||||
assert(evaled[0] === 'ey' || evaled[0] === 'el');
|
||||
});
|
||||
|
||||
it('TestInlineEvaluate', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('2.lg'));
|
||||
var evaled = LGFile.evaluate('hello');
|
||||
var templates = Templates.parseFile(GetExampleFilePath('2.lg'));
|
||||
var evaled = templates.evaluateText('hello');
|
||||
assert.strictEqual('hello', evaled);
|
||||
|
||||
evaled = LGFile.evaluate('${wPhrase()}');
|
||||
evaled = templates.evaluateText('${wPhrase()}');
|
||||
var options =[ 'Hi', 'Hello', 'Hiya' ];
|
||||
assert.strictEqual(options.includes(evaled), true);
|
||||
|
||||
var errMessage = '';
|
||||
try {
|
||||
LGFile.evaluate('${ErrrorTemplate()}');
|
||||
templates.evaluateText('${ErrrorTemplate()}');
|
||||
} catch (e) {
|
||||
errMessage = e.toString();
|
||||
}
|
||||
|
@ -463,77 +474,77 @@ describe('LG', function() {
|
|||
});
|
||||
|
||||
it('TestEvalExpression', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('EvalExpression.lg'));
|
||||
var templates = Templates.parseFile(GetExampleFilePath('EvalExpression.lg'));
|
||||
const userName = 'MS';
|
||||
var evaled = LGFile.evaluateTemplate('template1', {userName});
|
||||
var evaled = templates.evaluate('template1', {userName});
|
||||
assert.strictEqual(evaled, 'Hi MS', `Evaled is ${ evaled }`);
|
||||
|
||||
evaled = LGFile.evaluateTemplate('template2', {userName});
|
||||
evaled = templates.evaluate('template2', {userName});
|
||||
assert.strictEqual(evaled, 'Hi MS', `Evaled is ${ evaled }`);
|
||||
|
||||
evaled = LGFile.evaluateTemplate('template3', {userName});
|
||||
evaled = templates.evaluate('template3', {userName});
|
||||
assert.strictEqual(evaled, 'HiMS', `Evaled is ${ evaled }`);
|
||||
|
||||
const options1 = ['\r\nHi MS\r\n', '\nHi MS\n'];
|
||||
evaled = LGFile.evaluateTemplate('template4', { userName});
|
||||
evaled = templates.evaluate('template4', { userName});
|
||||
assert.strictEqual(options1.includes(evaled), true, `Evaled is ${ evaled }`);
|
||||
|
||||
const options2 = ['\r\nHiMS\r\n', '\nHiMS\n'];
|
||||
evaled = LGFile.evaluateTemplate('template5', { userName});
|
||||
evaled = templates.evaluate('template5', { userName});
|
||||
assert.strictEqual(options2.includes(evaled), true, `Evaled is ${ evaled }`);
|
||||
|
||||
evaled = LGFile.evaluateTemplate('template6', {userName});
|
||||
evaled = templates.evaluate('template6', {userName});
|
||||
assert.strictEqual(evaled, 'goodmorning', `Evaled is ${ evaled }`);
|
||||
});
|
||||
|
||||
|
||||
it('TestLGResource', function() {
|
||||
var lgResource = LGParser.parseText(fs.readFileSync(GetExampleFilePath('2.lg'), 'utf-8'));
|
||||
var templates = Templates.parseText(fs.readFileSync(GetExampleFilePath('2.lg'), 'utf-8'));
|
||||
|
||||
assert.strictEqual(lgResource.templates.length, 1);
|
||||
assert.strictEqual(lgResource.imports.length, 0);
|
||||
assert.strictEqual(lgResource.templates[0].name, 'wPhrase');
|
||||
assert.strictEqual(lgResource.templates[0].body.replace(/\r\n/g, '\n'), '> this is an in-template comment\n- Hi\n- Hello\n- Hiya\n- Hi');
|
||||
assert.strictEqual(templates.toArray().length, 1);
|
||||
assert.strictEqual(templates.imports.length, 0);
|
||||
assert.strictEqual(templates.toArray()[0].name, 'wPhrase');
|
||||
assert.strictEqual(templates.toArray()[0].body.replace(/\r\n/g, '\n'), '> this is an in-template comment\n- Hi\n- Hello\n- Hiya\n- Hi');
|
||||
|
||||
lgResource = lgResource.addTemplate('newtemplate', ['age', 'name'], '- hi ');
|
||||
assert.strictEqual(lgResource.templates.length, 2);
|
||||
assert.strictEqual(lgResource.imports.length, 0);
|
||||
assert.strictEqual(lgResource.templates[1].name, 'newtemplate');
|
||||
assert.strictEqual(lgResource.templates[1].parameters.length, 2);
|
||||
assert.strictEqual(lgResource.templates[1].parameters[0], 'age');
|
||||
assert.strictEqual(lgResource.templates[1].parameters[1], 'name');
|
||||
assert.strictEqual(lgResource.templates[1].body.replace(/\r\n/g, '\n'), '- hi \n');
|
||||
templates = templates.addTemplate('newtemplate', ['age', 'name'], '- hi ');
|
||||
assert.strictEqual(templates.toArray().length, 2);
|
||||
assert.strictEqual(templates.imports.length, 0);
|
||||
assert.strictEqual(templates.toArray()[1].name, 'newtemplate');
|
||||
assert.strictEqual(templates.toArray()[1].parameters.length, 2);
|
||||
assert.strictEqual(templates.toArray()[1].parameters[0], 'age');
|
||||
assert.strictEqual(templates.toArray()[1].parameters[1], 'name');
|
||||
assert.strictEqual(templates.toArray()[1].body.replace(/\r\n/g, '\n'), '- hi \n');
|
||||
|
||||
lgResource = lgResource.addTemplate('newtemplate2', undefined, '- hi2 ');
|
||||
assert.strictEqual(lgResource.templates.length, 3);
|
||||
assert.strictEqual(lgResource.templates[2].name, 'newtemplate2');
|
||||
assert.strictEqual(lgResource.templates[2].body.replace(/\r\n/g, '\n'), '- hi2 \n');
|
||||
templates = templates.addTemplate('newtemplate2', undefined, '- hi2 ');
|
||||
assert.strictEqual(templates.toArray().length, 3);
|
||||
assert.strictEqual(templates.toArray()[2].name, 'newtemplate2');
|
||||
assert.strictEqual(templates.toArray()[2].body.replace(/\r\n/g, '\n'), '- hi2 \n');
|
||||
|
||||
lgResource = lgResource.updateTemplate('newtemplate', 'newtemplateName', ['newage', 'newname'], '- new hi\r\n#hi');
|
||||
assert.strictEqual(lgResource.templates.length, 3);
|
||||
assert.strictEqual(lgResource.imports.length, 0);
|
||||
assert.strictEqual(lgResource.templates[1].name, 'newtemplateName');
|
||||
assert.strictEqual(lgResource.templates[1].parameters.length, 2);
|
||||
assert.strictEqual(lgResource.templates[1].parameters[0], 'newage');
|
||||
assert.strictEqual(lgResource.templates[1].parameters[1], 'newname');
|
||||
assert.strictEqual(lgResource.templates[1].body.replace(/\r\n/g, '\n'), '- new hi\n- #hi\n');
|
||||
templates = templates.updateTemplate('newtemplate', 'newtemplateName', ['newage', 'newname'], '- new hi\r\n#hi');
|
||||
assert.strictEqual(templates.toArray().length, 3);
|
||||
assert.strictEqual(templates.imports.length, 0);
|
||||
assert.strictEqual(templates.toArray()[1].name, 'newtemplateName');
|
||||
assert.strictEqual(templates.toArray()[1].parameters.length, 2);
|
||||
assert.strictEqual(templates.toArray()[1].parameters[0], 'newage');
|
||||
assert.strictEqual(templates.toArray()[1].parameters[1], 'newname');
|
||||
assert.strictEqual(templates.toArray()[1].body.replace(/\r\n/g, '\n'), '- new hi\n- #hi\n');
|
||||
|
||||
lgResource = lgResource.updateTemplate('newtemplate2', 'newtemplateName2', ['newage2', 'newname2'], '- new hi\r\n#hi2');
|
||||
assert.strictEqual(lgResource.templates.length, 3);
|
||||
assert.strictEqual(lgResource.imports.length, 0);
|
||||
assert.strictEqual(lgResource.templates[2].name, 'newtemplateName2');
|
||||
assert.strictEqual(lgResource.templates[2].body.replace(/\r\n/g, '\n'), '- new hi\n- #hi2\n');
|
||||
templates = templates.updateTemplate('newtemplate2', 'newtemplateName2', ['newage2', 'newname2'], '- new hi\r\n#hi2');
|
||||
assert.strictEqual(templates.toArray().length, 3);
|
||||
assert.strictEqual(templates.imports.length, 0);
|
||||
assert.strictEqual(templates.toArray()[2].name, 'newtemplateName2');
|
||||
assert.strictEqual(templates.toArray()[2].body.replace(/\r\n/g, '\n'), '- new hi\n- #hi2\n');
|
||||
|
||||
lgResource = lgResource.deleteTemplate('newtemplateName');
|
||||
assert.strictEqual(lgResource.templates.length, 2);
|
||||
templates = templates.deleteTemplate('newtemplateName');
|
||||
assert.strictEqual(templates.toArray().length, 2);
|
||||
|
||||
lgResource = lgResource.deleteTemplate('newtemplateName2');
|
||||
assert.strictEqual(lgResource.templates.length, 1);
|
||||
templates = templates.deleteTemplate('newtemplateName2');
|
||||
assert.strictEqual(templates.toArray().length, 1);
|
||||
});
|
||||
|
||||
it('TestMemoryScope', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('MemoryScope.lg'));
|
||||
var evaled = LGFile.evaluateTemplate('T1', { turn: { name: 'Dong', count: 3 } });
|
||||
var templates = Templates.parseFile(GetExampleFilePath('MemoryScope.lg'));
|
||||
var evaled = templates.evaluate('T1', { turn: { name: 'Dong', count: 3 } });
|
||||
assert.strictEqual(evaled, 'Hi Dong, welcome to Seattle, Seattle is a beautiful place, how many burgers do you want, 3?');
|
||||
|
||||
const objscope = {
|
||||
|
@ -545,55 +556,55 @@ describe('LG', function() {
|
|||
};
|
||||
var scope = new SimpleObjectMemory(objscope);
|
||||
|
||||
evaled = LGFile.evaluateTemplate('AskBread', scope);
|
||||
evaled = templates.evaluate('AskBread', scope);
|
||||
assert.strictEqual(evaled, 'Which Bread, A or B do you want?');
|
||||
});
|
||||
|
||||
it('TestStructuredTemplate', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('StructuredTemplate.lg'));
|
||||
var templates = Templates.parseFile(GetExampleFilePath('StructuredTemplate.lg'));
|
||||
|
||||
var evaled = LGFile.evaluateTemplate('AskForAge.prompt');
|
||||
var evaled = templates.evaluate('AskForAge.prompt');
|
||||
assert.equal(evaled.text, evaled.speak);
|
||||
|
||||
evaled = LGFile.evaluateTemplate('AskForAge.prompt2');
|
||||
evaled = templates.evaluate('AskForAge.prompt2');
|
||||
if (evaled.text.includes('how old')){
|
||||
assert.deepStrictEqual(evaled, JSON.parse('{"lgType":"Activity","text":"how old are you?","suggestedactions":["10","20","30"]}'));
|
||||
} else {
|
||||
assert.deepStrictEqual(evaled, JSON.parse('{"lgType":"Activity","text":"what\'s your age?","suggestedactions":["10","20","30"]}'));
|
||||
}
|
||||
|
||||
evaled = LGFile.evaluateTemplate('AskForAge.prompt3');
|
||||
evaled = templates.evaluate('AskForAge.prompt3');
|
||||
assert.deepStrictEqual(evaled, JSON.parse('{"lgType":"Activity","text":"${GetAge()}","suggestions":["10 | cards","20 | cards"]}'));
|
||||
|
||||
evaled = LGFile.evaluateTemplate('T1');
|
||||
evaled = templates.evaluate('T1');
|
||||
assert.deepStrictEqual(evaled, JSON.parse('{"lgType":"Activity","text":"This is awesome","speak":"foo bar I can also speak!"}'));
|
||||
|
||||
evaled = LGFile.evaluateTemplate('ST1');
|
||||
evaled = templates.evaluate('ST1');
|
||||
assert.deepStrictEqual(evaled, JSON.parse('{"lgType":"MyStruct","text":"foo","speak":"bar"}'));
|
||||
|
||||
evaled = LGFile.evaluateTemplate('AskForColor');
|
||||
evaled = templates.evaluate('AskForColor');
|
||||
assert.deepStrictEqual(evaled, JSON.parse('{"lgType":"Activity","suggestedactions":[{"lgType":"MyStruct","speak":"bar","text":"zoo"},{"lgType":"Activity","speak":"I can also speak!"}]}'));
|
||||
|
||||
evaled = LGFile.evaluateTemplate('MultiExpression');
|
||||
evaled = templates.evaluate('MultiExpression');
|
||||
assert.equal(evaled, '{"lgType":"Activity","speak":"I can also speak!"} {"lgType":"MyStruct","text":"hi"}');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('StructuredTemplateRef');
|
||||
evaled = templates.evaluate('StructuredTemplateRef');
|
||||
assert.deepStrictEqual(evaled, JSON.parse('{"lgType":"MyStruct","text":"hi"}'));
|
||||
|
||||
evaled = LGFile.evaluateTemplate('MultiStructuredRef');
|
||||
evaled = templates.evaluate('MultiStructuredRef');
|
||||
assert.deepStrictEqual(evaled, JSON.parse('{"lgType":"MyStruct","list":[{"lgType":"SubStruct","text":"hello"},{"lgType":"SubStruct","text":"world"}]}'));
|
||||
|
||||
evaled = LGFile.evaluateTemplate('templateWithSquareBrackets', {manufacturer: {Name : 'Acme Co'}});
|
||||
evaled = templates.evaluate('templateWithSquareBrackets', {manufacturer: {Name : 'Acme Co'}});
|
||||
assert.deepStrictEqual(evaled, JSON.parse('{"lgType":"Struct","text":"Acme Co"}'));
|
||||
|
||||
evaled = LGFile.evaluateTemplate('ValueWithEqualsMark', {name : 'Jack'});
|
||||
evaled = templates.evaluate('ValueWithEqualsMark', {name : 'Jack'});
|
||||
assert.deepStrictEqual(evaled, JSON.parse('{"lgType":"Activity","text":"Hello! welcome back. I have your name = Jack"}'));
|
||||
});
|
||||
|
||||
it('TestEvaluateOnce', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('EvaluateOnce.lg'));
|
||||
var templates = Templates.parseFile(GetExampleFilePath('EvaluateOnce.lg'));
|
||||
|
||||
var evaled = LGFile.evaluateTemplate('templateWithSameParams', { param: 'ms' });
|
||||
var evaled = templates.evaluate('templateWithSameParams', { param: 'ms' });
|
||||
assert.notEqual(evaled, undefined);
|
||||
|
||||
const resultList = evaled.split(' ');
|
||||
|
@ -602,29 +613,29 @@ describe('LG', function() {
|
|||
assert.equal(resultList[0], resultList[1]);
|
||||
|
||||
// maybe has different values
|
||||
evaled = LGFile.evaluateTemplate('templateWithDifferentParams', { param1: 'ms', param2: 'newms' });
|
||||
evaled = templates.evaluate('templateWithDifferentParams', { param1: 'ms', param2: 'newms' });
|
||||
});
|
||||
|
||||
it('TestConditionExpression', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('ConditionExpression.lg'));
|
||||
var templates = Templates.parseFile(GetExampleFilePath('ConditionExpression.lg'));
|
||||
|
||||
var evaled = LGFile.evaluateTemplate('conditionTemplate', { num: 1 });
|
||||
var evaled = templates.evaluate('conditionTemplate', { num: 1 });
|
||||
assert.equal(evaled, 'Your input is one');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('conditionTemplate', { num: 2 });
|
||||
evaled = templates.evaluate('conditionTemplate', { num: 2 });
|
||||
assert.equal(evaled, 'Your input is two');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('conditionTemplate', { num: 3 });
|
||||
evaled = templates.evaluate('conditionTemplate', { num: 3 });
|
||||
assert.equal(evaled, 'Your input is three');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('conditionTemplate', { num: 4 });
|
||||
evaled = templates.evaluate('conditionTemplate', { num: 4 });
|
||||
assert.equal(evaled, 'Your input is not one, two or three');
|
||||
});
|
||||
|
||||
it('TestExpandTemplateWithStructuredLG', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('StructuredTemplate.lg'));
|
||||
var templates = Templates.parseFile(GetExampleFilePath('StructuredTemplate.lg'));
|
||||
|
||||
var evaled = LGFile.expandTemplate('AskForAge.prompt');
|
||||
var evaled = templates.expandTemplate('AskForAge.prompt');
|
||||
assert.strictEqual(evaled.length, 4, `Evaled is ${ evaled }`);
|
||||
|
||||
let expectedResults = [
|
||||
|
@ -635,7 +646,7 @@ describe('LG', function() {
|
|||
];
|
||||
expectedResults.forEach( u => assert(evaled.includes(u)));
|
||||
|
||||
evaled = LGFile.expandTemplate('ExpanderT1');
|
||||
evaled = templates.expandTemplate('ExpanderT1');
|
||||
assert.strictEqual(evaled.length, 4, `Evaled is ${ evaled }`);
|
||||
|
||||
expectedResults = [
|
||||
|
@ -648,35 +659,35 @@ describe('LG', function() {
|
|||
});
|
||||
|
||||
it('TestExpressionextract', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('ExpressionExtract.lg'));
|
||||
var templates = Templates.parseFile(GetExampleFilePath('ExpressionExtract.lg'));
|
||||
|
||||
var evaled1 = LGFile.evaluateTemplate('templateWithBrackets');
|
||||
var evaled2 = LGFile.evaluateTemplate('templateWithBrackets2');
|
||||
var evaled3 = LGFile.evaluateTemplate('templateWithBrackets3').toString().trim();
|
||||
var evaled1 = templates.evaluate('templateWithBrackets');
|
||||
var evaled2 = templates.evaluate('templateWithBrackets2');
|
||||
var evaled3 = templates.evaluate('templateWithBrackets3').toString().trim();
|
||||
let espectedResult = 'don\'t mix {} and \'{}\'';
|
||||
assert.strictEqual(evaled1, espectedResult);
|
||||
assert.strictEqual(evaled2, espectedResult);
|
||||
assert.strictEqual(evaled3, espectedResult);
|
||||
|
||||
evaled1 = LGFile.evaluateTemplate('templateWithQuotationMarks');
|
||||
evaled2 = LGFile.evaluateTemplate('templateWithQuotationMarks2');
|
||||
evaled3 = LGFile.evaluateTemplate('templateWithQuotationMarks3').toString().trim();
|
||||
evaled1 = templates.evaluate('templateWithQuotationMarks');
|
||||
evaled2 = templates.evaluate('templateWithQuotationMarks2');
|
||||
evaled3 = templates.evaluate('templateWithQuotationMarks3').toString().trim();
|
||||
espectedResult = 'don\'t mix {"} and ""\'"';
|
||||
assert.strictEqual(evaled1, espectedResult);
|
||||
assert.strictEqual(evaled2, espectedResult);
|
||||
assert.strictEqual(evaled3, espectedResult);
|
||||
|
||||
evaled1 = LGFile.evaluateTemplate('templateWithUnpairedBrackets1');
|
||||
evaled2 = LGFile.evaluateTemplate('templateWithUnpairedBrackets12');
|
||||
evaled3 = LGFile.evaluateTemplate('templateWithUnpairedBrackets13').toString().trim();
|
||||
evaled1 = templates.evaluate('templateWithUnpairedBrackets1');
|
||||
evaled2 = templates.evaluate('templateWithUnpairedBrackets12');
|
||||
evaled3 = templates.evaluate('templateWithUnpairedBrackets13').toString().trim();
|
||||
espectedResult = '{prefix 5 sufix';
|
||||
assert.strictEqual(evaled1, espectedResult);
|
||||
assert.strictEqual(evaled2, espectedResult);
|
||||
assert.strictEqual(evaled3, espectedResult);
|
||||
|
||||
evaled1 = LGFile.evaluateTemplate('templateWithUnpairedBrackets2');
|
||||
evaled2 = LGFile.evaluateTemplate('templateWithUnpairedBrackets22');
|
||||
evaled3 = LGFile.evaluateTemplate('templateWithUnpairedBrackets23').toString().trim();
|
||||
evaled1 = templates.evaluate('templateWithUnpairedBrackets2');
|
||||
evaled2 = templates.evaluate('templateWithUnpairedBrackets22');
|
||||
evaled3 = templates.evaluate('templateWithUnpairedBrackets23').toString().trim();
|
||||
espectedResult = 'prefix 5 sufix}';
|
||||
assert.strictEqual(evaled1, espectedResult);
|
||||
assert.strictEqual(evaled2, espectedResult);
|
||||
|
@ -684,81 +695,81 @@ describe('LG', function() {
|
|||
});
|
||||
|
||||
it('TestEmptyArrayAndObject', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('EmptyArrayAndObject.lg'));
|
||||
var templates = Templates.parseFile(GetExampleFilePath('EmptyArrayAndObject.lg'));
|
||||
|
||||
var evaled = LGFile.evaluateTemplate('template', {list:[], obj: {}});
|
||||
var evaled = templates.evaluate('template', {list:[], obj: {}});
|
||||
assert.strictEqual(evaled, 'list and obj are both empty');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('template', {list:[], obj:new Map()});
|
||||
evaled = templates.evaluate('template', {list:[], obj:new Map()});
|
||||
assert.strictEqual(evaled, 'list and obj are both empty');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('template', {list:['hi'], obj: {}});
|
||||
evaled = templates.evaluate('template', {list:['hi'], obj: {}});
|
||||
assert.strictEqual(evaled, 'obj is empty');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('template', {list:[], obj: {a: 'a'}});
|
||||
evaled = templates.evaluate('template', {list:[], obj: {a: 'a'}});
|
||||
assert.strictEqual(evaled, 'list is empty');
|
||||
|
||||
const map = new Map();
|
||||
map.set('a', 'a');
|
||||
evaled = LGFile.evaluateTemplate('template', {list:[], obj: map});
|
||||
evaled = templates.evaluate('template', {list:[], obj: map});
|
||||
assert.strictEqual(evaled, 'list is empty');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('template', {list:[{}], obj : {a : 'a'}});
|
||||
evaled = templates.evaluate('template', {list:[{}], obj : {a : 'a'}});
|
||||
assert.strictEqual(evaled, 'list and obj are both not empty.');
|
||||
});
|
||||
|
||||
it('TestNullTolerant', function() {
|
||||
var lgFile = LGParser.parseFile(GetExampleFilePath('NullTolerant.lg'));
|
||||
var templates = Templates.parseFile(GetExampleFilePath('NullTolerant.lg'));
|
||||
|
||||
var evaled = lgFile.evaluateTemplate('template1');
|
||||
var evaled = templates.evaluate('template1');
|
||||
assert.strictEqual('null', evaled);
|
||||
|
||||
evaled = lgFile.evaluateTemplate('template2');
|
||||
evaled = templates.evaluate('template2');
|
||||
assert.strictEqual(`result is 'null'`, evaled);
|
||||
|
||||
var jObjEvaled = lgFile.evaluateTemplate('template3');
|
||||
var jObjEvaled = templates.evaluate('template3');
|
||||
assert.strictEqual('null', jObjEvaled['key1']);
|
||||
|
||||
});
|
||||
|
||||
|
||||
it('TestIsTemplateFunction', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('IsTemplate.lg'));
|
||||
var templates = Templates.parseFile(GetExampleFilePath('IsTemplate.lg'));
|
||||
|
||||
var evaled = LGFile.evaluateTemplate('template2', {templateName:'template1'});
|
||||
var evaled = templates.evaluate('template2', {templateName:'template1'});
|
||||
assert.strictEqual(evaled, 'template template1 exists');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('template2', {templateName:'wPhrase'});
|
||||
evaled = templates.evaluate('template2', {templateName:'wPhrase'});
|
||||
assert.strictEqual(evaled, 'template wPhrase exists');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('template2', {templateName:'xxx'});
|
||||
evaled = templates.evaluate('template2', {templateName:'xxx'});
|
||||
assert.strictEqual(evaled, 'template xxx does not exist');
|
||||
});
|
||||
|
||||
it('TestStringInterpolation', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('StringInterpolation.lg'));
|
||||
var templates = Templates.parseFile(GetExampleFilePath('StringInterpolation.lg'));
|
||||
|
||||
var evaled = LGFile.evaluateTemplate('simpleStringTemplate');
|
||||
var evaled = templates.evaluate('simpleStringTemplate');
|
||||
assert.strictEqual(evaled, 'say hi');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('StringTemplateWithVariable', {w:'world'});
|
||||
evaled = templates.evaluate('StringTemplateWithVariable', {w:'world'});
|
||||
assert.strictEqual(evaled, 'hello world');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('StringTemplateWithMixing', {name:'jack'});
|
||||
evaled = templates.evaluate('StringTemplateWithMixing', {name:'jack'});
|
||||
assert.strictEqual(evaled, 'I know your name is jack');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('StringTemplateWithJson', {h:'hello', w: 'world'});
|
||||
evaled = templates.evaluate('StringTemplateWithJson', {h:'hello', w: 'world'});
|
||||
assert.strictEqual(evaled, 'get \'h\' value : hello');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('StringTemplateWithEscape');
|
||||
evaled = templates.evaluate('StringTemplateWithEscape');
|
||||
assert.strictEqual(evaled, 'just want to output ${bala\`bala}');
|
||||
|
||||
evaled = LGFile.evaluateTemplate('StringTemplateWithTemplateRef');
|
||||
evaled = templates.evaluate('StringTemplateWithTemplateRef');
|
||||
assert.strictEqual(evaled, 'hello jack , welcome. nice weather!');
|
||||
});
|
||||
|
||||
it('TestMemoryAccessPath', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('MemoryAccess.lg'));
|
||||
var templates = Templates.parseFile(GetExampleFilePath('MemoryAccess.lg'));
|
||||
|
||||
const scope = {
|
||||
myProperty: {
|
||||
|
@ -776,21 +787,21 @@ describe('LG', function() {
|
|||
// this evaulate will hit memory access twice
|
||||
// first for "property", and get "p1", from local
|
||||
// sencond for "turn.property[p1].enum" and get "p1enum" from global
|
||||
var evaled = LGFile.evaluateTemplate('T1', scope);
|
||||
var evaled = templates.evaluate('T1', scope);
|
||||
assert.strictEqual(evaled, 'p1enum');
|
||||
|
||||
// this evaulate will hit memory access twice
|
||||
// first for "myProperty.name", and get "p1", from global
|
||||
// sencond for "turn.property[p1].enum" and get "p1enum" from global
|
||||
evaled = LGFile.evaluateTemplate('T3', scope);
|
||||
evaled = templates.evaluate('T3', scope);
|
||||
assert.strictEqual(evaled, 'p1enum');
|
||||
});
|
||||
|
||||
it('TestReExecute', function() {
|
||||
var LGFile = LGParser.parseFile(GetExampleFilePath('ReExecute.lg'));
|
||||
var templates = Templates.parseFile(GetExampleFilePath('ReExecute.lg'));
|
||||
|
||||
// may be has different values
|
||||
LGFile.evaluateTemplate('templateWithSameParams', {param1:'ms', param2:'newms'});
|
||||
templates.evaluate('templateWithSameParams', {param1:'ms', param2:'newms'});
|
||||
});
|
||||
|
||||
it('TestCustomFunction', function() {
|
||||
|
@ -804,11 +815,11 @@ describe('LG', function() {
|
|||
return Expression.lookup(func);
|
||||
}
|
||||
});
|
||||
let lgFile = LGParser.parseFile(GetExampleFilePath('CustomFunction.lg'), undefined, parser);
|
||||
assert.equal(lgFile.expressionParser, parser);
|
||||
let result = lgFile.evaluateTemplate('template', {});
|
||||
let templates = Templates.parseFile(GetExampleFilePath('CustomFunction.lg'), undefined, parser);
|
||||
assert.equal(templates.expressionParser, parser);
|
||||
let result = templates.evaluate('template', {});
|
||||
assert.strictEqual(result, 3);
|
||||
result = lgFile.evaluateTemplate('callSub', {});
|
||||
result = templates.evaluate('callSub', {});
|
||||
assert.strictEqual(result, 12);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
const { LGParser, DiagnosticSeverity, LGErrors } = require(`../`);
|
||||
const { Templates, DiagnosticSeverity, TemplateErrors } = require(`../`);
|
||||
const assert = require(`assert`);
|
||||
const fs = require(`fs`);
|
||||
|
||||
function GetExceptionExampleFilePath(fileName) {
|
||||
return `${ __dirname }/testData/exceptionExamples/` + fileName;
|
||||
|
@ -8,12 +7,12 @@ function GetExceptionExampleFilePath(fileName) {
|
|||
|
||||
function GetLGFile(fileName) {
|
||||
var filePath = GetExceptionExampleFilePath(fileName);
|
||||
return LGParser.parseFile(filePath);
|
||||
return Templates.parseFile(filePath);
|
||||
}
|
||||
|
||||
function GetDiagnostics(fileName) {
|
||||
var filePath = GetExceptionExampleFilePath(fileName);
|
||||
return LGParser.parseFile(filePath).diagnostics;
|
||||
return Templates.parseFile(filePath).diagnostics;
|
||||
}
|
||||
|
||||
describe(`LGExceptionTest`, function() {
|
||||
|
@ -21,25 +20,25 @@ describe(`LGExceptionTest`, function() {
|
|||
var diagnostics = GetDiagnostics(`ConditionFormatError.lg`);
|
||||
assert.strictEqual(diagnostics.length, 10);
|
||||
assert.strictEqual(diagnostics[0].severity, DiagnosticSeverity.Warning);
|
||||
assert.strictEqual(diagnostics[0].message.includes(LGErrors.notEndWithElseInCondition), true);
|
||||
assert.strictEqual(diagnostics[0].message.includes(TemplateErrors.notEndWithElseInCondition), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[1].severity);
|
||||
assert.strictEqual(diagnostics[1].message.includes(LGErrors.invalidExpressionInCondition), true);
|
||||
assert.strictEqual(diagnostics[1].message.includes(TemplateErrors.invalidExpressionInCondition), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[2].severity);
|
||||
assert.strictEqual(diagnostics[2].message.includes(LGErrors.multipleIfInCondition), true);
|
||||
assert.strictEqual(diagnostics[2].message.includes(TemplateErrors.multipleIfInCondition), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Warning, diagnostics[3].severity);
|
||||
assert.strictEqual(diagnostics[3].message.includes(LGErrors.notEndWithElseInCondition), true);
|
||||
assert.strictEqual(diagnostics[3].message.includes(TemplateErrors.notEndWithElseInCondition), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[4].severity);
|
||||
assert.strictEqual(diagnostics[4].message.includes(LGErrors.extraExpressionInCondition), true);
|
||||
assert.strictEqual(diagnostics[4].message.includes(TemplateErrors.extraExpressionInCondition), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[5].severity);
|
||||
assert.strictEqual(diagnostics[5].message.includes(LGErrors.multipleIfInCondition), true);
|
||||
assert.strictEqual(diagnostics[5].message.includes(TemplateErrors.multipleIfInCondition), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[6].severity);
|
||||
assert.strictEqual(diagnostics[6].message.includes(LGErrors.invalidMiddleInCondition), true);
|
||||
assert.strictEqual(diagnostics[6].message.includes(TemplateErrors.invalidMiddleInCondition), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[7].severity);
|
||||
assert.strictEqual(diagnostics[7].message.includes(LGErrors.invalidWhitespaceInCondition), true);
|
||||
assert.strictEqual(diagnostics[7].message.includes(TemplateErrors.invalidWhitespaceInCondition), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[8].severity);
|
||||
assert.strictEqual(diagnostics[8].message.includes(LGErrors.invalidWhitespaceInCondition), true);
|
||||
assert.strictEqual(diagnostics[8].message.includes(TemplateErrors.invalidWhitespaceInCondition), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Warning, diagnostics[9].severity);
|
||||
assert.strictEqual(diagnostics[9].message.includes(LGErrors.notStartWithIfInCondition), true);
|
||||
assert.strictEqual(diagnostics[9].message.includes(TemplateErrors.notStartWithIfInCondition), true);
|
||||
});
|
||||
|
||||
it(`TestDuplicatedTemplates`, function() {
|
||||
|
@ -47,18 +46,18 @@ describe(`LGExceptionTest`, function() {
|
|||
|
||||
assert.strictEqual(2, diagnostics.length);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[0].severity);
|
||||
assert.strictEqual(diagnostics[0].message.includes(LGErrors.duplicatedTemplateInSameTemplate('template1')), true);
|
||||
assert.strictEqual(diagnostics[0].message.includes(TemplateErrors.duplicatedTemplateInSameTemplate('template1')), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[1].severity);
|
||||
assert.strictEqual(diagnostics[1].message.includes(LGErrors.duplicatedTemplateInSameTemplate('template1')), true);
|
||||
assert.strictEqual(diagnostics[1].message.includes(TemplateErrors.duplicatedTemplateInSameTemplate('template1')), true);
|
||||
|
||||
var lgFile = GetLGFile(`DuplicatedTemplates.lg`);
|
||||
var allDiagnostics = lgFile.allDiagnostics;
|
||||
var templates = GetLGFile(`DuplicatedTemplates.lg`);
|
||||
var allDiagnostics = templates.allDiagnostics;
|
||||
|
||||
assert.strictEqual(4, allDiagnostics.length);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, allDiagnostics[0].severity);
|
||||
assert.strictEqual(allDiagnostics[0].message.includes(LGErrors.duplicatedTemplateInSameTemplate('template1')), true);
|
||||
assert.strictEqual(allDiagnostics[0].message.includes(TemplateErrors.duplicatedTemplateInSameTemplate('template1')), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, allDiagnostics[1].severity);
|
||||
assert.strictEqual(allDiagnostics[1].message.includes(LGErrors.duplicatedTemplateInSameTemplate('template1')), true);
|
||||
assert.strictEqual(allDiagnostics[1].message.includes(TemplateErrors.duplicatedTemplateInSameTemplate('template1')), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, allDiagnostics[2].severity);
|
||||
assert.strictEqual(allDiagnostics[2].message.includes(`Duplicated definitions found for template: 'basicTemplate'`), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, allDiagnostics[3].severity);
|
||||
|
@ -80,7 +79,7 @@ describe(`LGExceptionTest`, function() {
|
|||
|
||||
assert.strictEqual(1, diagnostics.length);
|
||||
assert.strictEqual(DiagnosticSeverity.Warning, diagnostics[0].severity);
|
||||
assert.strictEqual(diagnostics[0].message.includes(LGErrors.noTemplateBody('template')), true);
|
||||
assert.strictEqual(diagnostics[0].message.includes(TemplateErrors.noTemplateBody('template')), true);
|
||||
});
|
||||
|
||||
it(`TestErrorStructuredTemplate`, function() {
|
||||
|
@ -88,15 +87,15 @@ describe(`LGExceptionTest`, function() {
|
|||
|
||||
assert.strictEqual(5, diagnostics.length);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[0].severity);
|
||||
assert.strictEqual(diagnostics[0].message.includes(LGErrors.invalidStrucBody), true);
|
||||
assert.strictEqual(diagnostics[0].message.includes(TemplateErrors.invalidStrucBody), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[1].severity);
|
||||
assert.strictEqual(diagnostics[1].message.includes(LGErrors.emptyStrucContent), true);
|
||||
assert.strictEqual(diagnostics[1].message.includes(TemplateErrors.emptyStrucContent), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[2].severity);
|
||||
assert.strictEqual(diagnostics[2].message.includes(`Error occurred when parsing expression 'NOTemplate()'. NOTemplate does not have an evaluator`), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[3].severity);
|
||||
assert.strictEqual(diagnostics[3].message.includes(`Error occurred when parsing expression 'NOTemplate()'. NOTemplate does not have an evaluator`), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[4].severity);
|
||||
assert.strictEqual(diagnostics[4].message.includes(LGErrors.invalidStrucName), true);
|
||||
assert.strictEqual(diagnostics[4].message.includes(TemplateErrors.invalidStrucName), true);
|
||||
});
|
||||
|
||||
it(`TestErrorTemplateName`, function() {
|
||||
|
@ -106,7 +105,7 @@ describe(`LGExceptionTest`, function() {
|
|||
for(const diagnostic of diagnostics)
|
||||
{
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostic.severity);
|
||||
assert.strictEqual(diagnostic.message.includes(LGErrors.invalidTemplateName), true);
|
||||
assert.strictEqual(diagnostic.message.includes(TemplateErrors.invalidTemplateName), true);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -133,7 +132,7 @@ describe(`LGExceptionTest`, function() {
|
|||
|
||||
assert.strictEqual(1, diagnostics.length);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[0].severity);
|
||||
assert.strictEqual(diagnostics[0].message.includes(LGErrors.noEndingInMultiline), true);
|
||||
assert.strictEqual(diagnostics[0].message.includes(TemplateErrors.noEndingInMultiline), true);
|
||||
});
|
||||
|
||||
it(`TestNoNormalTemplateBody`, function() {
|
||||
|
@ -141,11 +140,11 @@ describe(`LGExceptionTest`, function() {
|
|||
|
||||
assert.strictEqual(3, diagnostics.length);
|
||||
assert.strictEqual(DiagnosticSeverity.Warning, diagnostics[0].severity);
|
||||
assert.strictEqual(diagnostics[0].message.includes(LGErrors.notEndWithElseInCondition), true);
|
||||
assert.strictEqual(diagnostics[0].message.includes(TemplateErrors.notEndWithElseInCondition), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[1].severity);
|
||||
assert.strictEqual(diagnostics[1].message.includes(LGErrors.missingTemplateBodyInCondition), true);
|
||||
assert.strictEqual(diagnostics[1].message.includes(TemplateErrors.missingTemplateBodyInCondition), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[2].severity);
|
||||
assert.strictEqual(diagnostics[2].message.includes(LGErrors.missingTemplateBodyInCondition), true);
|
||||
assert.strictEqual(diagnostics[2].message.includes(TemplateErrors.missingTemplateBodyInCondition), true);
|
||||
});
|
||||
|
||||
it(`TestNoTemplateRef`, function() {
|
||||
|
@ -166,76 +165,76 @@ describe(`LGExceptionTest`, function() {
|
|||
|
||||
assert.strictEqual(14, diagnostics.length);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[0].severity);
|
||||
assert.strictEqual(diagnostics[0].message.includes(LGErrors.invalidWhitespaceInSwitchCase), true);
|
||||
assert.strictEqual(diagnostics[0].message.includes(TemplateErrors.invalidWhitespaceInSwitchCase), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[1].severity);
|
||||
assert.strictEqual(diagnostics[1].message.includes(LGErrors.multipleSwithStatementInSwitchCase), true);
|
||||
assert.strictEqual(diagnostics[1].message.includes(TemplateErrors.multipleSwithStatementInSwitchCase), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[2].severity);
|
||||
assert.strictEqual(diagnostics[2].message.includes(LGErrors.notStartWithSwitchInSwitchCase), true);
|
||||
assert.strictEqual(diagnostics[2].message.includes(TemplateErrors.notStartWithSwitchInSwitchCase), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Warning, diagnostics[3].severity);
|
||||
assert.strictEqual(diagnostics[3].message.includes(LGErrors.missingCaseInSwitchCase), true);
|
||||
assert.strictEqual(diagnostics[3].message.includes(TemplateErrors.missingCaseInSwitchCase), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[4].severity);
|
||||
assert.strictEqual(diagnostics[4].message.includes(LGErrors.invalidStatementInMiddlerOfSwitchCase), true);
|
||||
assert.strictEqual(diagnostics[4].message.includes(TemplateErrors.invalidStatementInMiddlerOfSwitchCase), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Warning, diagnostics[5].severity);
|
||||
assert.strictEqual(diagnostics[5].message.includes(LGErrors.notEndWithDefaultInSwitchCase), true);
|
||||
assert.strictEqual(diagnostics[5].message.includes(TemplateErrors.notEndWithDefaultInSwitchCase), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[6].severity);
|
||||
assert.strictEqual(diagnostics[6].message.includes(LGErrors.extraExpressionInSwitchCase), true);
|
||||
assert.strictEqual(diagnostics[6].message.includes(TemplateErrors.extraExpressionInSwitchCase), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[7].severity);
|
||||
assert.strictEqual(diagnostics[7].message.includes(LGErrors.missingTemplateBodyInSwitchCase), true);
|
||||
assert.strictEqual(diagnostics[7].message.includes(TemplateErrors.missingTemplateBodyInSwitchCase), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[8].severity);
|
||||
assert.strictEqual(diagnostics[8].message.includes((LGErrors.extraExpressionInSwitchCase)), true);
|
||||
assert.strictEqual(diagnostics[8].message.includes((TemplateErrors.extraExpressionInSwitchCase)), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[9].severity);
|
||||
assert.strictEqual(diagnostics[9].message.includes(LGErrors.missingTemplateBodyInSwitchCase), true);
|
||||
assert.strictEqual(diagnostics[9].message.includes(TemplateErrors.missingTemplateBodyInSwitchCase), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[10].severity);
|
||||
assert.strictEqual(diagnostics[10].message.includes(LGErrors.invalidExpressionInSwiathCase), true);
|
||||
assert.strictEqual(diagnostics[10].message.includes(TemplateErrors.invalidExpressionInSwiathCase), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Error, diagnostics[11].severity);
|
||||
assert.strictEqual(diagnostics[11].message.includes(LGErrors.extraExpressionInSwitchCase), true);
|
||||
assert.strictEqual(diagnostics[11].message.includes(TemplateErrors.extraExpressionInSwitchCase), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Warning, diagnostics[12].severity);
|
||||
assert.strictEqual(diagnostics[12].message.includes(LGErrors.missingCaseInSwitchCase), true);
|
||||
assert.strictEqual(diagnostics[12].message.includes(TemplateErrors.missingCaseInSwitchCase), true);
|
||||
assert.strictEqual(DiagnosticSeverity.Warning, diagnostics[13].severity);
|
||||
assert.strictEqual(diagnostics[13].message.includes(LGErrors.notEndWithDefaultInSwitchCase), true);
|
||||
assert.strictEqual(diagnostics[13].message.includes(TemplateErrors.notEndWithDefaultInSwitchCase), true);
|
||||
});
|
||||
|
||||
it(`TestLoopDetected`, function() {
|
||||
var lgFile = GetLGFile(`LoopDetected.lg`);
|
||||
var templates = GetLGFile(`LoopDetected.lg`);
|
||||
|
||||
assert.throws(() => lgFile.evaluateTemplate(`wPhrase`), Error(`Loop detected: welcome-user => wPhrase[wPhrase] Error occurred when evaluating '-\${wPhrase()}'. [welcome-user] Error occurred when evaluating '-\${welcome-user()}'. `));
|
||||
assert.throws(() => templates.evaluate(`wPhrase`), Error(`Loop detected: welcome-user => wPhrase[wPhrase] Error occurred when evaluating '-\${wPhrase()}'. [welcome-user] Error occurred when evaluating '-\${welcome-user()}'. `));
|
||||
|
||||
assert.throws(() => lgFile.analyzeTemplate(`wPhrase`), Error('Loop detected: welcome-user => wPhrase'),);
|
||||
assert.throws(() => templates.analyzeTemplate(`wPhrase`), Error('Loop detected: welcome-user => wPhrase'),);
|
||||
});
|
||||
|
||||
it(`AddTextWithWrongId`, function() {
|
||||
var diagnostics = LGParser.parseText(`[import](xx.lg) \r\n # t \n - hi`, `a.lg`).diagnostics;
|
||||
var diagnostics = Templates.parseText(`[import](xx.lg) \r\n # t \n - hi`, `a.lg`).diagnostics;
|
||||
assert.strictEqual(1, diagnostics.length);
|
||||
assert.strictEqual(diagnostics[0].message.includes(`Could not find file`), true);
|
||||
});
|
||||
|
||||
it(`TestErrorExpression`, function() {
|
||||
var lgFile = GetLGFile(`ErrorExpression.lg`);
|
||||
assert.throws(() => lgFile.evaluateTemplate(`template1`), Error(`first(createArray(1, 2)) is neither a string nor a null object.[template1] Error occurred when evaluating '-\${length(first(createArray(1,2)))}'. `));
|
||||
var templates = GetLGFile(`ErrorExpression.lg`);
|
||||
assert.throws(() => templates.evaluate(`template1`), Error(`first(createArray(1, 2)) is neither a string nor a null object.[template1] Error occurred when evaluating '-\${length(first(createArray(1,2)))}'. `));
|
||||
});
|
||||
|
||||
it('TestRunTimeErrors', function() {
|
||||
var lgFile = GetLGFile('RunTimeErrors.lg');
|
||||
var templates = GetLGFile('RunTimeErrors.lg');
|
||||
|
||||
assert.throws(() => lgFile.evaluateTemplate(`template1`), Error(`'dialog.abc' evaluated to null. [template1] Error occurred when evaluating '-I want \${dialog.abc}'. `));
|
||||
assert.throws(() => templates.evaluate(`template1`), Error(`'dialog.abc' evaluated to null. [template1] Error occurred when evaluating '-I want \${dialog.abc}'. `));
|
||||
|
||||
assert.throws(() => lgFile.evaluateTemplate(`prebuilt1`), Error(`'dialog.abc' evaluated to null.[prebuilt1] Error occurred when evaluating '-I want \${foreach(dialog.abc, item, template1())}'. `));
|
||||
assert.throws(() => templates.evaluate(`prebuilt1`), Error(`'dialog.abc' evaluated to null.[prebuilt1] Error occurred when evaluating '-I want \${foreach(dialog.abc, item, template1())}'. `));
|
||||
|
||||
assert.throws(() => lgFile.evaluateTemplate(`template2`), Error(`'dialog.abc' evaluated to null. [template1] Error occurred when evaluating '-I want \${dialog.abc}'. [template2] Error occurred when evaluating '-With composition \${template1()}'. `));
|
||||
assert.throws(() => templates.evaluate(`template2`), Error(`'dialog.abc' evaluated to null. [template1] Error occurred when evaluating '-I want \${dialog.abc}'. [template2] Error occurred when evaluating '-With composition \${template1()}'. `));
|
||||
|
||||
assert.throws(() => lgFile.evaluateTemplate('conditionalTemplate1', { dialog : true }), Error(`'dialog.abc' evaluated to null. [template1] Error occurred when evaluating '-I want \${dialog.abc}'. [conditionalTemplate1] Condition '\${dialog}': Error occurred when evaluating '-I want \${template1()}'. `));
|
||||
assert.throws(() => templates.evaluate('conditionalTemplate1', { dialog : true }), Error(`'dialog.abc' evaluated to null. [template1] Error occurred when evaluating '-I want \${dialog.abc}'. [conditionalTemplate1] Condition '\${dialog}': Error occurred when evaluating '-I want \${template1()}'. `));
|
||||
|
||||
assert.throws(() => lgFile.evaluateTemplate(`conditionalTemplate2`), Error(`'dialog.abc' evaluated to null. [conditionalTemplate2] Condition '\${dialog.abc}': Error occurred when evaluating '-IF :\${dialog.abc}'. `));
|
||||
assert.throws(() => templates.evaluate(`conditionalTemplate2`), Error(`'dialog.abc' evaluated to null. [conditionalTemplate2] Condition '\${dialog.abc}': Error occurred when evaluating '-IF :\${dialog.abc}'. `));
|
||||
|
||||
assert.throws(() => lgFile.evaluateTemplate(`structured1`), Error(`'dialog.abc' evaluated to null. [structured1] Property 'Text': Error occurred when evaluating 'Text=I want \${dialog.abc}'. `));
|
||||
assert.throws(() => templates.evaluate(`structured1`), Error(`'dialog.abc' evaluated to null. [structured1] Property 'Text': Error occurred when evaluating 'Text=I want \${dialog.abc}'. `));
|
||||
|
||||
assert.throws(() => lgFile.evaluateTemplate(`structured2`), Error(`'dialog.abc' evaluated to null. [template1] Error occurred when evaluating '-I want \${dialog.abc}'. [structured2] Property 'Text': Error occurred when evaluating 'Text=I want \${template1()}'. `));
|
||||
assert.throws(() => templates.evaluate(`structured2`), Error(`'dialog.abc' evaluated to null. [template1] Error occurred when evaluating '-I want \${dialog.abc}'. [structured2] Property 'Text': Error occurred when evaluating 'Text=I want \${template1()}'. `));
|
||||
|
||||
assert.throws(() => lgFile.evaluateTemplate(`structured3`), Error(`'dialog.abc' evaluated to null. [template1] Error occurred when evaluating '-I want \${dialog.abc}'. [structured2] Property 'Text': Error occurred when evaluating 'Text=I want \${template1()}'. [structured3] Error occurred when evaluating '\${structured2()}'. `))
|
||||
assert.throws(() => templates.evaluate(`structured3`), Error(`'dialog.abc' evaluated to null. [template1] Error occurred when evaluating '-I want \${dialog.abc}'. [structured2] Property 'Text': Error occurred when evaluating 'Text=I want \${template1()}'. [structured3] Error occurred when evaluating '\${structured2()}'. `))
|
||||
|
||||
assert.throws(() => lgFile.evaluateTemplate(`switchcase1`, { turn : { testValue : 1 } }), Error(`'dialog.abc' evaluated to null. [switchcase1] Case '\${1}': Error occurred when evaluating '-I want \${dialog.abc}'. `));
|
||||
assert.throws(() => templates.evaluate(`switchcase1`, { turn : { testValue : 1 } }), Error(`'dialog.abc' evaluated to null. [switchcase1] Case '\${1}': Error occurred when evaluating '-I want \${dialog.abc}'. `));
|
||||
|
||||
assert.throws(() => lgFile.evaluateTemplate(`switchcase2`, { turn : { testValue : 0 } }), Error(`'dialog.abc' evaluated to null. [switchcase2] Case 'Default': Error occurred when evaluating '-I want \${dialog.abc}'. `));
|
||||
assert.throws(() => templates.evaluate(`switchcase2`, { turn : { testValue : 0 } }), Error(`'dialog.abc' evaluated to null. [switchcase2] Case 'Default': Error occurred when evaluating '-I want \${dialog.abc}'. `));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
# templatec
|
||||
- from a.en.lg
|
|
@ -0,0 +1,2 @@
|
|||
# templatec
|
||||
- from a.lg
|
|
@ -1,10 +0,0 @@
|
|||
# Greeting
|
||||
- Hi
|
||||
- Hello
|
||||
|
||||
#TimeOfDay
|
||||
- Morning
|
||||
- Evening
|
||||
|
||||
# FinalGreeting
|
||||
- ${Greeting()} ${TimeOfDay()}
|
|
@ -1,12 +0,0 @@
|
|||
#TimeOfDay
|
||||
- Morning
|
||||
- Evening
|
||||
- Afternoon
|
||||
|
||||
# TimeOfDayWithCondition
|
||||
- IF: ${time == 'morning'}
|
||||
- Have a good morning
|
||||
- ELSEIF: ${time == 'evening'}
|
||||
- Have a good evening
|
||||
- ELSE:
|
||||
- Have a good afternoon
|
|
@ -1,23 +0,0 @@
|
|||
# Greeting
|
||||
- Hey
|
||||
- Hi
|
||||
|
||||
# TimeOfDayWithCondition
|
||||
- IF: ${time == 'morning'}
|
||||
- ${Greeting()} Morning
|
||||
- ELSEIF: ${time == 'evening'}
|
||||
- ${Greeting()} Evening
|
||||
- ELSE:
|
||||
- ${Greeting()} Afternoon
|
||||
|
||||
# greetInAWeek
|
||||
- SWITCH: ${day}
|
||||
- CASE: ${'Saturday'}
|
||||
- Happy Saturday!
|
||||
- Nice Saturday!
|
||||
- CASE: ${'Sunday'}
|
||||
- Happy Sunday!
|
||||
- Nice Sunday!
|
||||
- DEFAULT:
|
||||
- Work Hard!
|
||||
- Weekend soon!
|
|
@ -1,5 +0,0 @@
|
|||
# ST2
|
||||
[MyStruct
|
||||
Speak = bar
|
||||
Text = zoo
|
||||
]
|
|
@ -1,5 +0,0 @@
|
|||
# ST2
|
||||
[MyStruct
|
||||
Speak = hello
|
||||
Text = world
|
||||
]
|
|
@ -1,19 +0,0 @@
|
|||
> Error template
|
||||
# template
|
||||
|
||||
> No match rule
|
||||
# template2
|
||||
- IF: ${foo == 'bar'}
|
||||
- ok
|
||||
|
||||
# template3
|
||||
-CASE: ${'bar'}
|
||||
- bar
|
||||
|
||||
# template4
|
||||
- SWITCH:${foo}
|
||||
- default:
|
||||
-bar
|
||||
|
||||
# template(param1 param2)
|
||||
- hello
|
|
@ -1,118 +0,0 @@
|
|||
> Text and Speak should get the same result
|
||||
# AskForAge.prompt
|
||||
[Activity
|
||||
Text = ${GetAge()}
|
||||
> this is a comment about this specific property
|
||||
Speak = ${GetAge()}
|
||||
]
|
||||
|
||||
|
||||
# GetAge
|
||||
- how old are you?
|
||||
- what's your age?
|
||||
|
||||
|
||||
> With '|' you are making attachments a list.
|
||||
# AskForAge.prompt2
|
||||
[Activity
|
||||
Text = ${GetAge()}
|
||||
SuggestedActions = 10 | 20 | 30
|
||||
]
|
||||
|
||||
|
||||
> You can use '\' as an escape character
|
||||
# AskForAge.prompt3
|
||||
[Activity
|
||||
Text = ${GetAge()}
|
||||
Suggestions = 10 \| cards | 20 \| cards
|
||||
]
|
||||
|
||||
|
||||
> Tab and whitespace is support in front of property
|
||||
> can access the property of another structured result
|
||||
> and whitespace inb front of the end square bracket is allowed
|
||||
# T1
|
||||
[Activity
|
||||
Text = ${T2()}
|
||||
Speak = foo bar ${T3().speak}
|
||||
]
|
||||
|
||||
|
||||
# T2
|
||||
- This is awesome
|
||||
|
||||
|
||||
# T3
|
||||
[Activity
|
||||
Speak = I can also speak!
|
||||
]
|
||||
|
||||
|
||||
> use a pure to get the structured, but remember, with the same property, original one would be hold
|
||||
> so, the result of Text would be 'foo' but not 'zoo'
|
||||
# ST1
|
||||
[MyStruct
|
||||
Text = foo
|
||||
${ST2()}
|
||||
]
|
||||
|
||||
|
||||
# ST2
|
||||
[MyStruct
|
||||
Speak = bar
|
||||
Text = zoo
|
||||
]
|
||||
|
||||
|
||||
> each item can also be a structure
|
||||
# AskForColor
|
||||
[Activity
|
||||
SuggestedActions = ${ST2()} | ${T3()}
|
||||
]
|
||||
|
||||
|
||||
> if you use multi structures in a normal template body, the result would be a string result
|
||||
> but not a list with two items
|
||||
# MultiExpression
|
||||
- ${T3()} ${T4()}
|
||||
|
||||
|
||||
> template can ref to another steuctured template
|
||||
# StructuredTemplateRef
|
||||
- ${T4()}
|
||||
|
||||
|
||||
# T4
|
||||
[MyStruct
|
||||
Text = hi
|
||||
]
|
||||
|
||||
|
||||
> if you want to re-use the structured, foreach function is a good way
|
||||
# MultiStructuredRef
|
||||
[MyStruct
|
||||
list = ${foreach(createArray('hello','world'), x, T5(x))}
|
||||
]
|
||||
|
||||
|
||||
# T5(text)
|
||||
[SubStruct
|
||||
Text = ${text}
|
||||
]
|
||||
|
||||
# ExpanderT1
|
||||
[MyStruct
|
||||
Text = ${ExpanderT2()}
|
||||
${ExpanderT3()}
|
||||
]
|
||||
|
||||
|
||||
# ExpanderT2
|
||||
- Hi
|
||||
- Hello
|
||||
|
||||
# ExpanderT3
|
||||
[MyStruct
|
||||
Speak = ${GetAge()}
|
||||
Text = zoo
|
||||
]
|
|
@ -1,47 +0,0 @@
|
|||
# ShowAlarm(alarm)
|
||||
- ${alarm.time} at ${alarm.date}
|
||||
- ${alarm.time} of ${alarm.date}
|
||||
|
||||
# ShowAlarmsWithForeach
|
||||
- IF: ${count(alarms) == 1}
|
||||
- You have one alarm ${ShowAlarm(alarms[0])}
|
||||
- ELSEIF: ${count(alarms) == 2}
|
||||
- You have ${count(alarms)} alarms, ${join(foreach(alarms, alarm, ShowAlarm(alarm)), ', ', ' and ')}
|
||||
- ELSE:
|
||||
- You don't have any alarms
|
||||
|
||||
# ShowAlarmsWithLgTemplate
|
||||
- IF: ${count(alarms) == 1}
|
||||
- You have one alarm ${ShowAlarm(alarms[0])}
|
||||
- ELSEIF: ${count(alarms) == 2}
|
||||
- You have ${count(alarms)} alarms, they are ${ShowAlarm(alarms[1])}
|
||||
- ELSE:
|
||||
- You don't have any alarms
|
||||
|
||||
# ShowAlarmsWithMultiLine
|
||||
-```
|
||||
You have ${count(alarms)} alarms.
|
||||
They are ${ShowAlarm(alarms[1])}
|
||||
```
|
||||
|
||||
# bookTransportTicket
|
||||
-SWITCH:${pass}
|
||||
- CASE: ${'Flight'}
|
||||
- Flight ticket booked
|
||||
- CASE: ${'Train'}
|
||||
- Train ticket booked
|
||||
- DEFAULT:
|
||||
- Shuttle ticket booked
|
||||
|
||||
# T1
|
||||
- Hey
|
||||
- Hello
|
||||
|
||||
# T2
|
||||
- ${length(T1())}
|
||||
|
||||
# T3
|
||||
- ${count(T1())}
|
||||
|
||||
# T4
|
||||
- ${substring(T1(), 1, 2)}
|
Загрузка…
Ссылка в новой задаче