add error messages for rule syntax

This commit is contained in:
Sorrel Bri 2020-02-19 22:14:21 -08:00
parent 66be6e0650
commit 54dbe75a70
3 changed files with 137 additions and 80 deletions

View file

@ -15,8 +15,8 @@ export const initState = (changesArgument: number): stateType => {
'a>ɯ/._#', 'a>ɯ/._#',
'[+ sonorant - low rounded high back]>0/._.', '[+ sonorant - low rounded high back]>0/._.',
'[+ obstruent]>[+ obstruent aspirated ]/#_.', '[+ obstruent]>[+ obstruent aspirated ]/#_.',
'[+ sonorant - rounded]>[+ sonorant + rounded]/._#' '[+ sonorant - rounded]>[+ sonorant + rounded]/._#',
// 'nn>nun/._.', 'nn>nun/._.'
] ]
} }
], ],

View file

@ -64,12 +64,28 @@ const findFeaturesFromGrapheme = (phones: {}, lexeme:string): [] => {
return featureBundle; return featureBundle;
} }
const decomposeRule = (rule: string): ruleBundle => { const errorMessage = ([prefix, separator], location, err) => `${prefix}${location}${separator}${err}`
const lintRule = (rule) => {
if (rule.match(/>/g) === null) throw `Insert '>' operator between target and result`
if (rule.match(/\//g) === null) throw `Insert '/' operator between change and environment`
if (rule.match(/_/g) === null) throw `Insert '_' operator in environment`
if (rule.match(/>/g).length > 1) throw `Too many '>' operators`
if (rule.match(/\//g).length > 1) throw `Too many '/' operators`
if (rule.match(/_/g).length > 1) throw `Too many '_' operators`
return rule.split(/>|\/|_/g);
}
const decomposeRule = (rule: string, index: number): ruleBundle => {
// splits rule at '>' '/' and '_' substrings resulting in array of length 4 // splits rule at '>' '/' and '_' substrings resulting in array of length 4
const [position, newFeatures, pre, post] = rule.split(/>|\/|_/g); try {
return { const [position, newFeatures, pre, post] = lintRule(rule);
environment: { pre, position, post }, return {
newFeatures environment: { pre, position, post },
newFeatures
}
} catch (err) {
throw errorMessage`Error in line ${index + 1}: ${err}`;
} }
} }
@ -125,7 +141,13 @@ const mapRuleBundleToFeatureBundle = phones => ruleBundle => {
export const decomposeRules = (epoch: epochType, phones: {[key: string]: phoneType}): decomposedRulesType => { export const decomposeRules = (epoch: epochType, phones: {[key: string]: phoneType}): decomposedRulesType => {
const { changes } = epoch const { changes } = epoch
return changes.map(decomposeRule).map(mapRuleBundleToFeatureBundle(phones)); try {
return changes
.map(decomposeRule)
.map(mapRuleBundleToFeatureBundle(phones));
} catch (err) {
return err;
}
} }
const isPhonemeBoundByRule = phonemeFeatures => (ruleFeature, index) => { const isPhonemeBoundByRule = phonemeFeatures => (ruleFeature, index) => {

View file

@ -14,77 +14,54 @@ describe('Results', () => {
}); });
it('rules decomposed properly', () => { it('rules decomposed properly', () => {
const epoch = initState().epochs[0]; const { epochs, phones } = initState(1);
epoch.changes = epoch.changes.slice(0,1) const result = getResult();
const phones = initState().phones; expect(decomposeRules(epochs[0], phones)).toStrictEqual(result);
const result = [
{
// ! '[+ occlusive - nasal]>[+ occlusive nasal]/n_',
environment: {
pre: [
{
sonorant: true, nasal: true, occlusive: true, coronal: true
}
],
position: [
{occlusive: true, nasal: false}
],
post: [],
},
newFeatures: [{occlusive: true, nasal: true}]
}
];
expect(decomposeRules(epoch, phones)).toStrictEqual(result);
}); });
it('expect transform lexeme to apply rule to lexeme', () => { it('rule without ">" returns helpful error message', () => {
const lexemeBundle = [ const { phones } = initState();
{ const epoch = { name: 'error epoch', changes: [ 't/n/_' ] }
grapheme: 'a', expect(decomposeRules(epoch, phones)).toEqual("Error in line 1: Insert '>' operator between target and result");
features: {
sonorant: true,
back: true,
low: true,
high: false,
rounded: false
}
},
{
grapheme: 'n',
features: { sonorant: true, nasal: true, occlusive: true, coronal: true }
},
{
grapheme: 't',
features: { occlusive: true, coronal: true, obstruent: true, nasal: false }
},
{
grapheme: 'a',
features: {
sonorant: true,
back: true,
low: true,
high: false,
rounded: false
}
}
]
const resultsLexeme = [...lexemeBundle]
resultsLexeme[2] = lexemeBundle[1]
const rule = {
environment: {
pre: [ { sonorant: true, nasal: true, occlusive: true, coronal: true } ],
position: [ { occlusive: true, nasal: false } ],
post: []
},
newFeatures: [ { occlusive: true, nasal: true } ]
}
expect(transformLexeme(lexemeBundle, rule, initState().features)).toEqual(resultsLexeme)
}) })
it('rule with too many ">" returns helpful error message', () => {
const { phones } = initState();
const epoch = { name: 'error epoch', changes: [ 't>n>/_' ] }
expect(decomposeRules(epoch, phones)).toEqual("Error in line 1: Too many '>' operators");
})
it('rule without "/" returns helpful error message', () => {
const { phones } = initState();
const epoch = { name: 'error epoch', changes: [ 't>n_' ] }
expect(decomposeRules(epoch, phones)).toEqual("Error in line 1: Insert '/' operator between change and environment");
})
it('rule with too many "/" returns helpful error message', () => {
const { phones } = initState();
const epoch = { name: 'error epoch', changes: [ 't>n/_/' ] }
expect(decomposeRules(epoch, phones)).toEqual("Error in line 1: Too many '/' operators");
})
it('rule without "_" returns helpful error message', () => {
const { phones } = initState();
const epoch = { name: 'error epoch', changes: [ 't>n/' ] }
expect(decomposeRules(epoch, phones)).toEqual("Error in line 1: Insert '_' operator in environment");
})
it('rule with too many "_" returns helpful error message', () => {
const { phones } = initState();
const epoch = { name: 'error epoch', changes: [ 't>n/__' ] }
expect(decomposeRules(epoch, phones)).toEqual("Error in line 1: Too many '_' operators");
})
it('expect transform lexeme to apply rule to lexeme', () => {
const lexemeBundle = getlexemeBundle();
const resultsLexeme = [...lexemeBundle]
resultsLexeme[2] = lexemeBundle[1]
const rule = getRule();
expect(transformLexeme(lexemeBundle, rule, initState().features)).toEqual(resultsLexeme)
})
it('results returned from first sound change rule', () => { it('results returned from first sound change rule', () => {
const action = {type: 'RUN'}; const action = {type: 'RUN'};
@ -150,19 +127,77 @@ describe('Results', () => {
} }
]); ]);
}); });
// if('results returned from sound change suite', () => {
// it('results returned through sixth sound change rule', () => {
// const action = {type: 'RUN'}; // const action = {type: 'RUN'};
// state = initState() // state = initState(5)
// console.log(stateReducer(state, action).results)
// expect(stateReducer(state, action).results).toEqual([ // expect(stateReducer(state, action).results).toEqual([
// { // {
// pass: 'epoch 1', // pass: 'epoch 1',
// lexicon: [ // lexicon: [
// 'anna', 'anta', 'anət', 'anna', 'tan', 'ənna' // 'anunu', 'anat', 'ant', 'anunu', 'tʰan', 'nunu'
// ] // ]
// } // }
// ]); // ]);
// }); // });
}); });
const getlexemeBundle = () => ([
{
grapheme: 'a',
features: {
sonorant: true,
back: true,
low: true,
high: false,
rounded: false
}
},
{
grapheme: 'n',
features: { sonorant: true, nasal: true, occlusive: true, coronal: true }
},
{
grapheme: 't',
features: { occlusive: true, coronal: true, obstruent: true, nasal: false }
},
{
grapheme: 'a',
features: {
sonorant: true,
back: true,
low: true,
high: false,
rounded: false
}
}
])
const getRule = () => ({
environment: {
pre: [ { sonorant: true, nasal: true, occlusive: true, coronal: true } ],
position: [ { occlusive: true, nasal: false } ],
post: []
},
newFeatures: [ { occlusive: true, nasal: true } ]
})
const getResult = () => ([
{
environment: {
pre: [
{
sonorant: true, nasal: true, occlusive: true, coronal: true
}
],
position: [
{occlusive: true, nasal: false}
],
post: [],
},
newFeatures: [{occlusive: true, nasal: true}]
}
]);