add error messages for rule syntax
This commit is contained in:
parent
66be6e0650
commit
54dbe75a70
3 changed files with 137 additions and 80 deletions
|
@ -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/._.'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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'};
|
||||||
|
@ -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'};
|
// 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}]
|
||||||
|
}
|
||||||
|
]);
|
Loading…
Reference in a new issue