UNPKG

eliza-core

Version:

A rendition of ELIZA program engine by Weizenbaum sharable for all javascript environments

122 lines (121 loc) 4.76 kB
import * as EString from './estring'; import { NoMentionDefException } from './exceptions'; import { cartesian } from './utils'; const NAMING_CHARACTERS = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' + 'abcdefghijklmnopqrstuvwxyz' + '0123456789-_').split(''); function validateMentionNaming(name) { return name.split('').find(c => NAMING_CHARACTERS.indexOf(c) < 0) === undefined; } function segmentScope(pattern) { const patternProfile = []; let restToSegment = pattern; while (true) { const segmentedPat = EString.match(restToSegment, '*@*[*]*'); if (!segmentedPat) { patternProfile.push({ pattern: restToSegment }); break; } if (validateMentionNaming(segmentedPat[1])) { patternProfile.push({ pattern: segmentedPat[0] }); patternProfile.push({ pattern: segmentedPat[2], mentions: [segmentedPat[1]] }); restToSegment = segmentedPat[3]; continue; } const searchMentionNamePosition = segmentedPat[1].lastIndexOf('@'); if (searchMentionNamePosition > -1) { patternProfile.push({ pattern: segmentedPat[0] + '@' + segmentedPat[1].substring(0, searchMentionNamePosition), }); patternProfile.push({ pattern: segmentedPat[2], mentions: [segmentedPat[1].substring(searchMentionNamePosition + 1)], }); restToSegment = segmentedPat[3]; continue; } patternProfile.push({ pattern: restToSegment }); } return patternProfile; } function cartesianAllScopes(synonyms, patternProfile) { return patternProfile.reduce((agg, current) => { if (agg.length < 1) { return [[current]]; } if (current.mentions) { let possibleWords = []; current.mentions.forEach(mentionTag => { const mentionRoute = synonyms.find(synonym => synonym.tag === mentionTag); if (mentionRoute) { possibleWords = possibleWords.concat(mentionRoute.words.map(word => ({ pattern: word, mentionTag, innerPattern: current.pattern }))); } else { throw new NoMentionDefException(mentionTag); } }); return cartesian(agg, possibleWords).map(comb => [...comb[0], comb[1]]); } else { return cartesian(agg, [current]).map(comb => [...comb[0], comb[1]]); } }, []); } export function matchDecomposition(synonyms, str, pat) { const patternProfile = segmentScope(pat); if (patternProfile.length < 3) { const simpleMatch = EString.match(str, pat); return simpleMatch ? { slottedTokens: simpleMatch.map(t => ({ token: t, scopes: {}, })), scopes: {}, } : null; } const cartesianAllSyn = cartesianAllScopes(synonyms, patternProfile); let matchedParts = []; const matchedPattern = cartesianAllSyn.find(patternParts => { const matchAttempt = EString.match(str, patternParts.map(p => p.pattern).join('')); if (matchAttempt) { matchedParts = matchAttempt; } return !!matchAttempt; }); if (!matchedPattern) { return null; } const ensuredParts = matchedParts; const hyperDecomposeRes = { slottedTokens: [], scopes: {}, }; matchedPattern.forEach(p => { const expectedParts = EString.count(p.pattern, '*'); if (p.mentionTag && p.innerPattern) { const mentionTag = p.mentionTag; const innerDecomposition = matchDecomposition(synonyms, p.pattern, p.innerPattern); if (!innerDecomposition) { throw new Error(`Fatal Error: Decomposing in scope failed: [${p.pattern}] --> [${p.innerPattern}]`); } innerDecomposition.slottedTokens .forEach(part => { part.scopes[mentionTag] = { text: p.pattern, mentionTag: p.mentionTag, }; hyperDecomposeRes.slottedTokens.push(part); }); hyperDecomposeRes.scopes[mentionTag] = { text: p.pattern, mentionTag: p.mentionTag, }; return; } for (let index = 0; index < expectedParts; index++) { const part = ensuredParts.shift(); if (part === undefined || part === null) { throw new Error('Fatal Error: Extracted Terms not matching wildcards!'); } hyperDecomposeRes.slottedTokens.push({ token: part, scopes: {} }); } }); return hyperDecomposeRes; }