diff --git a/src/reducers/reducer.init.js b/src/reducers/reducer.init.js index 5da2a86..24943cf 100644 --- a/src/reducers/reducer.init.js +++ b/src/reducers/reducer.init.js @@ -5,15 +5,15 @@ export type initAction = { type: "INIT" } -export const initState = (changesArgument: number = -1): stateType => { +export const initState = (changesArgument: number): stateType => { const state = { epochs: [ { name: 'epoch 1', changes: [ '[+ occlusive - nasal]>[+ occlusive + nasal]/n_.', - // 'at>ta/._#', - // '[+ sonorant - low rounded high back]>0/._.', + 'a>ɯ/._#', + '[+ sonorant - low rounded high back]>0/._.', // 'nn>nun/._.', // '[+ nasal][+ obstruent]>[+ nasal obstruent aspirated ]/#_.', // '[+ sonorant rounded]>[+ sonorant - rounded]/._#' @@ -43,7 +43,7 @@ export const initState = (changesArgument: number = -1): stateType => { }, t: { grapheme: 't', features: { - occlusive: true, coronal: true, obstruent: true + occlusive: true, coronal: true, obstruent: true, nasal: false }, ʰ: { grapheme: 'tʰ', features: { @@ -86,7 +86,7 @@ export const initState = (changesArgument: number = -1): stateType => { {lexeme: 'ənta', epoch: state.epochs[0]} ] - if(changesArgument > -1) state.epochs[0].changes = state.epochs[0].changes.splice(changesArgument, 1) + if(changesArgument > -1) state.epochs[0].changes = state.epochs[0].changes.splice(0, changesArgument) return state; } \ No newline at end of file diff --git a/src/reducers/reducer.results.js b/src/reducers/reducer.results.js index 344495d..45d9c96 100644 --- a/src/reducers/reducer.results.js +++ b/src/reducers/reducer.results.js @@ -43,6 +43,7 @@ const findFeaturesFromLexeme = (phones: {}, lexeme:string): [] => { }) return featureBundle; } + const findFeaturesFromGrapheme = (phones: {}, lexeme:string): [] => { let featureBundle = [] let lastIndex = lexeme.length - 1; @@ -94,6 +95,8 @@ const mapToPositiveAndNegativeFeatures = phoneme => ( const mapStringToFeatures = (ruleString, phones) => { if (ruleString) { if (ruleString === '.') return []; + if (ruleString === '#') return ['#'] + if (ruleString === '0') return []; const ruleBrackets = ruleString.match(/\[.*\]/) if (ruleBrackets) { return ruleString @@ -129,6 +132,7 @@ const isPhonemeBoundByRule = phonemeFeatures => (ruleFeature, index) => { const phoneme = phonemeFeatures[index].features; return Object.entries(ruleFeature).reduce((bool, [feature, value]) => { if (!bool) return false; + if (!phoneme.hasOwnProperty(feature)) return false; if (!phoneme[feature] && !value) return true; if (phoneme[feature] !== value) return false; return true; @@ -141,9 +145,8 @@ const isEnvironmentBoundByRule = (phonemeFeatures, ruleFeatures) => { ? true : false; } - - const swapPhoneme = (phoneme, newFeatures, features) => { + if (!newFeatures) return {} const newPhonemeFeatures = Object.entries(newFeatures) .reduce((newPhoneme, [newFeature, newValue]) => ({ ...newPhoneme, [newFeature]: newValue }) , {...phoneme.features}); @@ -154,12 +157,30 @@ const swapPhoneme = (phoneme, newFeatures, features) => { , newPhonemeCandidates[newPhonemeCandidates.length - 1])[0]; } +const transformLexemeInitial = (newLexeme, pre, post, position, phoneme, index, lexemeBundle, newFeatures, features) => { + if (index !== pre.length - 1) return [...newLexeme, phoneme]; + if (!isEnvironmentBoundByRule([phoneme], position)) return [...newLexeme, phoneme]; + if (!isEnvironmentBoundByRule(lexemeBundle.slice(index, index + post.length), post)) return [...newLexeme, phoneme]; + const newPhoneme = swapPhoneme(phoneme, newFeatures[0], features); + return [...newLexeme, newPhoneme]; +} + +const transformLexemeCoda = (newLexeme, pre, post, position, phoneme, index, lexemeBundle, newFeatures, features) => { + if (index + post.length !== lexemeBundle.length) return [...newLexeme, phoneme]; + if (!isEnvironmentBoundByRule(lexemeBundle.slice(index - pre.length, index), pre)) return [...newLexeme, phoneme]; + if (!isEnvironmentBoundByRule([phoneme], position)) return [...newLexeme, phoneme]; + const newPhoneme = swapPhoneme(phoneme, newFeatures[0], features); + return [...newLexeme, newPhoneme]; +} + export const transformLexeme = (lexemeBundle, rule, features) => { const {pre, post, position} = rule.environment; const newLexeme = lexemeBundle.reduce((newLexeme, phoneme, index) => { + if (pre.find(val => val === '#')) return transformLexemeInitial(newLexeme, pre, post, position, phoneme, index, lexemeBundle, rule.newFeatures, features); + if (post.find(val => val === '#')) return transformLexemeCoda(newLexeme, pre, post, position, phoneme, index, lexemeBundle, rule.newFeatures, features); if ( index < pre.length || index >= lexemeBundle.length - post.length ) return [...newLexeme, phoneme]; if (!isEnvironmentBoundByRule(lexemeBundle.slice(index - pre.length, index), pre)) return [...newLexeme, phoneme]; - if (!isEnvironmentBoundByRule([phoneme], rule.environment.position)) return [...newLexeme, phoneme]; + if (!isEnvironmentBoundByRule([phoneme], position)) return [...newLexeme, phoneme]; if (!isEnvironmentBoundByRule(lexemeBundle.slice(index, index + post.length), post)) return [...newLexeme, phoneme]; const newPhoneme = swapPhoneme(phoneme, rule.newFeatures[0], features); return [...newLexeme, newPhoneme]; @@ -173,7 +194,7 @@ const transformLexicon = lexiconBundle => ruleBundle => features => lexiconBundle.map(lexemeBundle => ruleBundle.reduce( - (lexeme, rule) => transformLexeme(lexeme, rule, features) + (lexeme, rule, i) => transformLexeme(lexeme, rule, features) , lexemeBundle )) diff --git a/src/reducers/reducer.results.test.js b/src/reducers/reducer.results.test.js index cfdaa6b..cd62116 100644 --- a/src/reducers/reducer.results.test.js +++ b/src/reducers/reducer.results.test.js @@ -55,7 +55,7 @@ describe('Results', () => { }, { grapheme: 't', - features: { occlusive: true, coronal: true, obstruent: true } + features: { occlusive: true, coronal: true, obstruent: true, nasal: false } }, { grapheme: 'a', @@ -88,7 +88,7 @@ describe('Results', () => { it('results returned from first sound change rule', () => { const action = {type: 'RUN'}; - state = initState(0) + state = initState(1) expect(stateReducer(state, action).results).toEqual([ { pass: 'epoch 1', @@ -99,18 +99,44 @@ describe('Results', () => { ]); }); - if('results returned from sound change suite', () => { + it('results returned through second sound change rule', () => { const action = {type: 'RUN'}; - state = initState() - console.log(stateReducer(state, action).results) + state = initState(2) expect(stateReducer(state, action).results).toEqual([ { pass: 'epoch 1', lexicon: [ - 'anna', 'anat', 'anət', 'anna', 'tan', 'ənna' + 'annɯ', 'anat', 'anət', 'annɯ', 'tan', 'ənnɯ' ] } ]); }); -}); \ No newline at end of file + it('results returned through third sound change rule', () => { + const action = {type: 'RUN'}; + state = initState(3) + expect(stateReducer(state, action).results).toEqual([ + { + pass: 'epoch 1', + lexicon: [ + 'annɯ', 'anat', 'ant', 'annɯ', 'tan', 'nnɯ' + ] + } + ]); + }); + + // if('results returned from sound change suite', () => { + // const action = {type: 'RUN'}; + // state = initState() + // console.log(stateReducer(state, action).results) + // expect(stateReducer(state, action).results).toEqual([ + // { + // pass: 'epoch 1', + // lexicon: [ + // 'anna', 'anta', 'anət', 'anna', 'tan', 'ənna' + // ] + // } + // ]); + // }); + +});