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>ɯ/._#',
'[+ sonorant - low rounded high back]>0/._.',
'[+ obstruent]>[+ obstruent aspirated ]/#_.',
'[+ sonorant - rounded]>[+ sonorant + rounded]/._#'
// 'nn>nun/._.',
'[+ sonorant - rounded]>[+ sonorant + rounded]/._#',
'nn>nun/._.'
]
}
],

View file

@ -64,12 +64,28 @@ const findFeaturesFromGrapheme = (phones: {}, lexeme:string): [] => {
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
const [position, newFeatures, pre, post] = rule.split(/>|\/|_/g);
return {
environment: { pre, position, post },
newFeatures
try {
const [position, newFeatures, pre, post] = lintRule(rule);
return {
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 => {
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) => {

View file

@ -14,77 +14,54 @@ describe('Results', () => {
});
it('rules decomposed properly', () => {
const epoch = initState().epochs[0];
epoch.changes = epoch.changes.slice(0,1)
const phones = initState().phones;
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);
const { epochs, phones } = initState(1);
const result = getResult();
expect(decomposeRules(epochs[0], phones)).toStrictEqual(result);
});
it('expect transform lexeme to apply rule to lexeme', () => {
const lexemeBundle = [
{
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 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 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 target and result");
})
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', () => {
const action = {type: 'RUN'};
@ -151,18 +128,76 @@ describe('Results', () => {
]);
});
// if('results returned from sound change suite', () => {
// it('results returned through sixth sound change rule', () => {
// const action = {type: 'RUN'};
// state = initState()
// console.log(stateReducer(state, action).results)
// state = initState(5)
// expect(stateReducer(state, action).results).toEqual([
// {
// pass: 'epoch 1',
// 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}]
}
]);