in experimental/generation/generator/packages/library/src/substitutions.ts [68:164]
function substitutions(name: string, bindings: any, copies?: number, seed?: string): string {
if (!copies) copies = 1
if (!seed) seed = '0'
// Normalize bindings into a simple array and setup variables
const state = new Map<string, Variable>();
for (const [binding, value] of Object.entries(bindings)) {
if (Array.isArray(value)) {
state.set(binding, {index: -1, values: (value as any).flat()})
} else if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
state.set(binding, {index: -1, values: value ? [value] : []})
}
}
// Track all generated utterances across calls in bindings
bindings.utterances ??= new Set<string>()
const utterances = bindings.utterances as Set<string>
const result: string[] = []
const rand = random(seed)
const file = DataCache.get(name)
if (file) {
let lines = file.split(os.EOL)
if (lines.length < 2) {
// Windows uses CRLF and that is how it is checked-in, but when an npm
// package is built it switches to just LF.
lines = file.split('\n')
}
const replacer = (line: string): {newLine: string, missing: boolean} => {
let missing = false
const newLine = line.replace(/\${([^}*?]+)[*?]?\}/g,
(match, key) => {
const val = binding(key, state, rand)
if (!val) {
missing = true
}
// For conditional tests value is empty but we set missing
return match.endsWith('?}') ? '' : val
})
return {newLine, missing}
}
let skipToNextComment = false
for (const line of lines) {
const isComment = line.startsWith('>')
if (isComment) {
const {newLine, missing} = replacer(line)
skipToNextComment = missing
if (!missing) {
result.push(newLine)
}
} else if (!skipToNextComment) {
if (line.trim() === '') {
result.push(line)
} else {
const all = line.match(/\${[^}*]+\*}/g) ?? []
for (const [key, variable] of state) {
variable.index = all.includes(`\${${key}*}`) ? 0 : -1
}
do {
let missing = false
for (let i = 0; i < copies; ++i) {
// Number of times to try for a unique result
let tries = 3
do {
const newline = line.replace(/\${([^}*?]+)[*?]?\}/g,
(match, key) => {
const val = binding(key, state, rand)
if (!val) {
missing = true
}
// For conditional tests value is empty but we set missing
return match.endsWith('?}') ? '' : val
})
if (missing) {
// If missing a value, drop the line
tries = 0
i = copies
break
}
tries = tries - 1
const text = plainText(newline)
// Ensure we generate given text only once because otherwise we can label it in multiple ways which LUIS
// does not allow
if (!utterances.has(text)) {
utterances.add(text)
result.push(newline)
tries = 0
}
} while (tries > 0)
}
} while (!increment(state))
}
}
}
}
return result.join(os.EOL)
}