add error check for unknown tokens in feature portions of rules

This commit is contained in:
Sorrel Bri 2020-02-19 23:18:18 -08:00
parent 54dbe75a70
commit 6885aeba2f
2 changed files with 71 additions and 35 deletions

View file

@ -89,20 +89,39 @@ const decomposeRule = (rule: string, index: number): ruleBundle => {
}
}
const doesFeatureRuleContainUnknownToken = features => {
const unknownTokens = features
.match(/\W/g)
.filter(v => v !== '-' && v !== '+' && v !== ']' && v !== '[' && v !== ' ')
if (unknownTokens.length) throw `Unknown token '${unknownTokens[0]}'`;
return true
}
const getPositiveFeatures = phoneme => {
const positiveFeatures = phoneme.match(/(?=\+.).*(?<=\-)|(?=\+.).*(?!\-).*(?<=\])/g)
return positiveFeatures ? positiveFeatures[0]
.trim().match(/\w+/g)
.reduce((map, feature) => ({...map, [feature]: true}), {})
: {}
try {
const positiveFeatures = phoneme.match(/(?=\+.).*(?<=\-)|(?=\+.).*(?!\-).*(?<=\])/g)
if (positiveFeatures) doesFeatureRuleContainUnknownToken(positiveFeatures[0])
return positiveFeatures ? positiveFeatures[0]
.trim().match(/\w+/g)
.reduce((map, feature) => ({...map, [feature]: true}), {})
: {}
} catch (err) {
throw err;
}
}
const getNegativeFeatures = phoneme => {
const negativeFeatures = phoneme.match(/(?=\-.).*(?<=\+)|(?=\-.).*(?!\+).*(?<=\])/g)
return negativeFeatures ? negativeFeatures[0]
.trim().match(/\w+/g)
.reduce((map, feature) => ({...map, [feature]: false}), {})
: {}
try {
const negativeFeatures = phoneme.match(/(?=\-.).*(?<=\+)|(?=\-.).*(?!\+).*(?<=\])/g)
if (negativeFeatures) doesFeatureRuleContainUnknownToken(negativeFeatures[0])
return negativeFeatures ? negativeFeatures[0]
.trim()
.match(/\w+/g)
.reduce((map, feature) => ({...map, [feature]: false}), {})
: {}
} catch (err) {
throw err;
}
}
const mapToPositiveAndNegativeFeatures = phoneme => (
@ -114,28 +133,36 @@ const mapStringToFeatures = (ruleString, phones) => {
if (ruleString === '#') return ['#']
if (ruleString === '0') return [];
const ruleBrackets = ruleString.match(/\[.*\]/)
if (ruleBrackets) {
return ruleString
try {
if (ruleBrackets) {
return ruleString
.split('[')
// filter out empty strings
.filter(v => v)
.map(mapToPositiveAndNegativeFeatures)
}
return findFeaturesFromGrapheme(phones, ruleString);
} catch (err) {
throw err;
}
return findFeaturesFromGrapheme(phones, ruleString);
}
return {};
}
const mapRuleBundleToFeatureBundle = phones => ruleBundle => {
const mapRuleBundleToFeatureBundle = phones => ( ruleBundle, index ) => {
// for each object in ruleBundle, map values to array of objects with feature-boolean key-value pairs
const { newFeatures, environment:{ pre, position, post } } = ruleBundle;
return {
environment: {
pre: mapStringToFeatures(pre, phones),
position: mapStringToFeatures(position, phones),
post: mapStringToFeatures(post, phones),
},
newFeatures: mapStringToFeatures(newFeatures, phones)
try {
const { newFeatures, environment:{ pre, position, post } } = ruleBundle;
return {
environment: {
pre: mapStringToFeatures(pre, phones),
position: mapStringToFeatures(position, phones),
post: mapStringToFeatures(post, phones),
},
newFeatures: mapStringToFeatures(newFeatures, phones)
}
} catch (err) {
throw errorMessage`Error in line ${index + 1}: ${err}`;
}
}
@ -228,18 +255,21 @@ const stringifyResults = lexemeBundle => Object.entries(lexemeBundle).map(getGra
export const run = (state: stateType, action: resultsAction): stateType => {
// TODO iterate through each epoch
const epoch = state.epochs[0];
const { phones, lexicon, features } = state;
const ruleBundle = decomposeRules(epoch, phones);
const lexiconBundle = formBundleFromLexicon(lexicon)(phones);
const passResults = transformLexicon(lexiconBundle)(ruleBundle)(features);
const stringifiedPassResults = passResults.map(stringifyResults);
const pass = {
pass: epoch.name,
lexicon: stringifiedPassResults
try {
const epoch = state.epochs[0];
const { phones, lexicon, features } = state;
const ruleBundle = decomposeRules(epoch, phones);
const lexiconBundle = formBundleFromLexicon(lexicon)(phones);
const passResults = transformLexicon(lexiconBundle)(ruleBundle)(features);
const stringifiedPassResults = passResults.map(stringifyResults);
const pass = {
pass: epoch.name,
lexicon: stringifiedPassResults
}
return {...state, results: [pass] }
} catch (err) {
return err;
}
return {...state, results: [pass] }
}

View file

@ -55,6 +55,12 @@ describe('Results', () => {
expect(decomposeRules(epoch, phones)).toEqual("Error in line 1: Too many '_' operators");
})
it('rule with incorrect feature syntax returns helpful error message', () => {
const { phones } = initState();
const epoch = { name: 'error epoch', changes: [ '[+ occlusive - nasal = obstruent]>n/_' ] }
expect(decomposeRules(epoch, phones)).toEqual("Error in line 1: Unknown token '='");
})
it('expect transform lexeme to apply rule to lexeme', () => {
const lexemeBundle = getlexemeBundle();
const resultsLexeme = [...lexemeBundle]