diff --git a/src/reducers/stateReducer.results.js b/src/reducers/stateReducer.results.js index 76df8b5..f1b2e79 100644 --- a/src/reducers/stateReducer.results.js +++ b/src/reducers/stateReducer.results.js @@ -1,11 +1,22 @@ // @flow -import type { stateType } from './stateReducer'; +import type { stateType, epochType, phoneType } from './stateReducer'; export type resultsAction = { type: 'RUN' } -const findFeatures = (phones: {}, lexeme:string): [] => { +export type decomposedRulesType = [ + { + environment: { + pre: [{[key: string]: boolean}], + position: [{[key: string]: boolean}], + post: [{[key: string]: boolean}] + }, + newFeatures: [{[key: string]: boolean}] + } +] + +const findFeaturesFromLexeme = (phones: {}, lexeme:string): [] => { let featureBundle = [] let lastIndex = lexeme.length - 1; let node = {}; @@ -23,12 +34,80 @@ const findFeatures = (phones: {}, lexeme:string): [] => { }) return featureBundle; } +const findFeaturesFromGrapheme = (phones: {}, lexeme:string): [] => { + let featureBundle = [] + let lastIndex = lexeme.length - 1; + let node = {}; + [...lexeme].forEach((graph, index) => { + if (!index && !lastIndex) featureBundle.push(phones[graph].features) + if (!index) return node = phones[graph] + if (index === lastIndex) return node[graph] + ? featureBundle.push(node[graph]) + : featureBundle.push(node, phones[graph]) + if (!node[graph] && node.features) { + featureBundle.push(node) + return node = phones[graph] + } + if (!node[graph]) + return node = node[graph] + }) + return featureBundle; +} -const decomposeRule = (rule: string): string[] => { +const decomposeRule = (rule: string) => { let decomposedChange = rule.split('>'); decomposedChange = [decomposedChange[0], ...decomposedChange[1].split('/')] decomposedChange = [decomposedChange[0], decomposedChange[1], ...decomposedChange[2].split('_')]; - return [...decomposedChange]; + const ruleBundle = { + environment: { + pre: decomposedChange[2], + position: decomposedChange[0], + post: decomposedChange[3] + }, + newFeatures: decomposedChange[1] + } + return ruleBundle; +} + +const mapStringToFeatures = (ruleString, phones) => { + if (ruleString) { + const ruleBrackets = ruleString.match(/\[.*\]/) + if (ruleBrackets) { + const ruleFeatures = ruleString.match(/(?!\[).*(? ({[feature]: true})) + const negativeFeatures = ruleFeatures.slice(minusIndex +1).trim().split(' '); + const negativeFeaturesMap = negativeFeatures.map(feature => ({[feature]: false})) + return {...positiveFeaturesMap, ...negativeFeaturesMap} + } + const grapheme = ruleString; + return findFeaturesFromGrapheme(phones, grapheme); + } + return {}; +} + +const mapRuleBundleToFeatureBundle = (ruleBundle, phones) => { + // ! for each object in ruleBundle, map values to array of objects with feature-boolean key-value pairs + const featureBundle = {...ruleBundle}; + console.log(featureBundle) + featureBundle.environment.pre = mapStringToFeatures(featureBundle.environment.pre, phones); + console.log(featureBundle.environment.pre) + featureBundle.environment.position = mapStringToFeatures(featureBundle.environment.position, phones); + console.log(featureBundle.environment.position) + featureBundle.environment.post = mapStringToFeatures(featureBundle.environment.post, phones); + console.log(featureBundle.environment.post) + featureBundle.newFeatures = mapStringToFeatures(featureBundle.newFeatures, phones); + console.log(featureBundle.newFeatures) + return featureBundle; +} + +export const decomposeRules = (epoch: epochType, phones: {[key: string]: phoneType}): decomposedRulesType => { + let ruleBundle = [...epoch.changes] + ruleBundle = epoch.changes.map(rule => decomposeRule(rule)); + const featureBundle = ruleBundle.map(rule => mapRuleBundleToFeatureBundle(rule, phones)); + return featureBundle; } export const run = (state: stateType, action: resultsAction): stateType => { @@ -43,7 +122,7 @@ export const run = (state: stateType, action: resultsAction): stateType => { }) }) - let featurePhoneBundle = state.lexicon.map(lexeme => findFeatures(state.phones, lexeme)) + let featurePhoneBundle = state.lexicon.map(lexeme => findFeaturesFromLexeme(state.phones, lexeme)) console.log(featurePhoneBundle) ruleBundle.forEach(rule => { diff --git a/src/reducers/stateReducer.results.test.js b/src/reducers/stateReducer.results.test.js index 7bdab9b..b93cc63 100644 --- a/src/reducers/stateReducer.results.test.js +++ b/src/reducers/stateReducer.results.test.js @@ -1,5 +1,6 @@ -import {stateReducer} from './stateReducer'; -import {initState} from './stateReducer.init'; +import { stateReducer } from './stateReducer'; +import { initState } from './stateReducer.init'; +import { decomposeRules } from './stateReducer.results'; describe('Results', () => { let state = {}; @@ -12,15 +13,39 @@ describe('Results', () => { expect(stateReducer(state, action)).toBe(state); }); - it('results returned from first sound change rule', () => { - const action = {type: 'RUN'}; - state = initState(0) - expect(stateReducer(state, action).results).toEqual({ - pass: 'epoch 1', - results: [ - 'anna', 'anat', 'anət', 'anna', 'tan', 'ənna' - ] - }) - }); + 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)).toBe(result); + }) + + // it('results returned from first sound change rule', () => { + // const action = {type: 'RUN'}; + // state = initState(0) + // expect(stateReducer(state, action).results).toEqual({ + // pass: 'epoch 1', + // results: [ + // 'anna', 'anat', 'anət', 'anna', 'tan', 'ənna' + // ] + // }) + // }); }); \ No newline at end of file