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 getPositiveFeatures = phoneme => {
try {
const positiveFeatures = phoneme.match(/(?=\+.).*(?<=\-)|(?=\+.).*(?!\-).*(?<=\])/g) const positiveFeatures = phoneme.match(/(?=\+.).*(?<=\-)|(?=\+.).*(?!\-).*(?<=\])/g)
if (positiveFeatures) doesFeatureRuleContainUnknownToken(positiveFeatures[0])
return positiveFeatures ? positiveFeatures[0] return positiveFeatures ? positiveFeatures[0]
.trim().match(/\w+/g) .trim().match(/\w+/g)
.reduce((map, feature) => ({...map, [feature]: true}), {}) .reduce((map, feature) => ({...map, [feature]: true}), {})
: {} : {}
} catch (err) {
throw err;
}
} }
const getNegativeFeatures = phoneme => { const getNegativeFeatures = phoneme => {
try {
const negativeFeatures = phoneme.match(/(?=\-.).*(?<=\+)|(?=\-.).*(?!\+).*(?<=\])/g) const negativeFeatures = phoneme.match(/(?=\-.).*(?<=\+)|(?=\-.).*(?!\+).*(?<=\])/g)
if (negativeFeatures) doesFeatureRuleContainUnknownToken(negativeFeatures[0])
return negativeFeatures ? negativeFeatures[0] return negativeFeatures ? negativeFeatures[0]
.trim().match(/\w+/g) .trim()
.match(/\w+/g)
.reduce((map, feature) => ({...map, [feature]: false}), {}) .reduce((map, feature) => ({...map, [feature]: false}), {})
: {} : {}
} catch (err) {
throw err;
}
} }
const mapToPositiveAndNegativeFeatures = phoneme => ( const mapToPositiveAndNegativeFeatures = phoneme => (
@ -114,6 +133,7 @@ const mapStringToFeatures = (ruleString, phones) => {
if (ruleString === '#') return ['#'] if (ruleString === '#') return ['#']
if (ruleString === '0') return []; if (ruleString === '0') return [];
const ruleBrackets = ruleString.match(/\[.*\]/) const ruleBrackets = ruleString.match(/\[.*\]/)
try {
if (ruleBrackets) { if (ruleBrackets) {
return ruleString return ruleString
.split('[') .split('[')
@ -122,12 +142,16 @@ const mapStringToFeatures = (ruleString, phones) => {
.map(mapToPositiveAndNegativeFeatures) .map(mapToPositiveAndNegativeFeatures)
} }
return findFeaturesFromGrapheme(phones, ruleString); return findFeaturesFromGrapheme(phones, ruleString);
} catch (err) {
throw err;
}
} }
return {}; 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 // for each object in ruleBundle, map values to array of objects with feature-boolean key-value pairs
try {
const { newFeatures, environment:{ pre, position, post } } = ruleBundle; const { newFeatures, environment:{ pre, position, post } } = ruleBundle;
return { return {
environment: { environment: {
@ -137,6 +161,9 @@ const mapRuleBundleToFeatureBundle = phones => ruleBundle => {
}, },
newFeatures: mapStringToFeatures(newFeatures, phones) newFeatures: mapStringToFeatures(newFeatures, phones)
} }
} catch (err) {
throw errorMessage`Error in line ${index + 1}: ${err}`;
}
} }
export const decomposeRules = (epoch: epochType, phones: {[key: string]: phoneType}): decomposedRulesType => { export const decomposeRules = (epoch: epochType, phones: {[key: string]: phoneType}): decomposedRulesType => {
@ -228,8 +255,8 @@ const stringifyResults = lexemeBundle => Object.entries(lexemeBundle).map(getGra
export const run = (state: stateType, action: resultsAction): stateType => { export const run = (state: stateType, action: resultsAction): stateType => {
// TODO iterate through each epoch // TODO iterate through each epoch
try {
const epoch = state.epochs[0]; const epoch = state.epochs[0];
const { phones, lexicon, features } = state; const { phones, lexicon, features } = state;
const ruleBundle = decomposeRules(epoch, phones); const ruleBundle = decomposeRules(epoch, phones);
@ -242,4 +269,7 @@ export const run = (state: stateType, action: resultsAction): stateType => {
} }
return {...state, results: [pass] } return {...state, results: [pass] }
} catch (err) {
return err;
}
} }

View file

@ -55,6 +55,12 @@ describe('Results', () => {
expect(decomposeRules(epoch, phones)).toEqual("Error in line 1: Too many '_' operators"); 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', () => { it('expect transform lexeme to apply rule to lexeme', () => {
const lexemeBundle = getlexemeBundle(); const lexemeBundle = getlexemeBundle();
const resultsLexeme = [...lexemeBundle] const resultsLexeme = [...lexemeBundle]