add error check for unknown tokens in feature portions of rules
This commit is contained in:
parent
54dbe75a70
commit
6885aeba2f
2 changed files with 71 additions and 35 deletions
|
@ -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 => {
|
||||||
const positiveFeatures = phoneme.match(/(?=\+.).*(?<=\-)|(?=\+.).*(?!\-).*(?<=\])/g)
|
try {
|
||||||
return positiveFeatures ? positiveFeatures[0]
|
const positiveFeatures = phoneme.match(/(?=\+.).*(?<=\-)|(?=\+.).*(?!\-).*(?<=\])/g)
|
||||||
.trim().match(/\w+/g)
|
if (positiveFeatures) doesFeatureRuleContainUnknownToken(positiveFeatures[0])
|
||||||
.reduce((map, feature) => ({...map, [feature]: true}), {})
|
return positiveFeatures ? positiveFeatures[0]
|
||||||
: {}
|
.trim().match(/\w+/g)
|
||||||
|
.reduce((map, feature) => ({...map, [feature]: true}), {})
|
||||||
|
: {}
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getNegativeFeatures = phoneme => {
|
const getNegativeFeatures = phoneme => {
|
||||||
const negativeFeatures = phoneme.match(/(?=\-.).*(?<=\+)|(?=\-.).*(?!\+).*(?<=\])/g)
|
try {
|
||||||
return negativeFeatures ? negativeFeatures[0]
|
const negativeFeatures = phoneme.match(/(?=\-.).*(?<=\+)|(?=\-.).*(?!\+).*(?<=\])/g)
|
||||||
.trim().match(/\w+/g)
|
if (negativeFeatures) doesFeatureRuleContainUnknownToken(negativeFeatures[0])
|
||||||
.reduce((map, feature) => ({...map, [feature]: false}), {})
|
return negativeFeatures ? negativeFeatures[0]
|
||||||
: {}
|
.trim()
|
||||||
|
.match(/\w+/g)
|
||||||
|
.reduce((map, feature) => ({...map, [feature]: false}), {})
|
||||||
|
: {}
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapToPositiveAndNegativeFeatures = phoneme => (
|
const mapToPositiveAndNegativeFeatures = phoneme => (
|
||||||
|
@ -114,28 +133,36 @@ 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(/\[.*\]/)
|
||||||
if (ruleBrackets) {
|
try {
|
||||||
return ruleString
|
if (ruleBrackets) {
|
||||||
|
return ruleString
|
||||||
.split('[')
|
.split('[')
|
||||||
// filter out empty strings
|
// filter out empty strings
|
||||||
.filter(v => v)
|
.filter(v => v)
|
||||||
.map(mapToPositiveAndNegativeFeatures)
|
.map(mapToPositiveAndNegativeFeatures)
|
||||||
|
}
|
||||||
|
return findFeaturesFromGrapheme(phones, ruleString);
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
return findFeaturesFromGrapheme(phones, ruleString);
|
|
||||||
}
|
}
|
||||||
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
|
||||||
const { newFeatures, environment:{ pre, position, post } } = ruleBundle;
|
try {
|
||||||
return {
|
const { newFeatures, environment:{ pre, position, post } } = ruleBundle;
|
||||||
environment: {
|
return {
|
||||||
pre: mapStringToFeatures(pre, phones),
|
environment: {
|
||||||
position: mapStringToFeatures(position, phones),
|
pre: mapStringToFeatures(pre, phones),
|
||||||
post: mapStringToFeatures(post, phones),
|
position: mapStringToFeatures(position, phones),
|
||||||
},
|
post: mapStringToFeatures(post, phones),
|
||||||
newFeatures: mapStringToFeatures(newFeatures, 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 => {
|
export const run = (state: stateType, action: resultsAction): stateType => {
|
||||||
|
|
||||||
// TODO iterate through each epoch
|
// TODO iterate through each epoch
|
||||||
const epoch = state.epochs[0];
|
try {
|
||||||
|
const epoch = state.epochs[0];
|
||||||
|
const { phones, lexicon, features } = state;
|
||||||
|
|
||||||
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
|
||||||
|
}
|
||||||
|
|
||||||
const ruleBundle = decomposeRules(epoch, phones);
|
return {...state, results: [pass] }
|
||||||
const lexiconBundle = formBundleFromLexicon(lexicon)(phones);
|
} catch (err) {
|
||||||
const passResults = transformLexicon(lexiconBundle)(ruleBundle)(features);
|
return err;
|
||||||
const stringifiedPassResults = passResults.map(stringifyResults);
|
|
||||||
const pass = {
|
|
||||||
pass: epoch.name,
|
|
||||||
lexicon: stringifiedPassResults
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {...state, results: [pass] }
|
|
||||||
}
|
}
|
|
@ -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]
|
||||||
|
|
Loading…
Reference in a new issue