* 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:
Hongyang Du (hond) 2020-03-11 03:34:18 +08:00 коммит произвёл GitHub
Родитель 4d5b305515
Коммит 42e366d9ea
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
35 изменённых файлов: 1579 добавлений и 1535 удалений

Просмотреть файл

@ -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)}