wohoo blocks working
This commit is contained in:
parent
560387d969
commit
96b91e6336
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@lingdocs/pashto-inflector",
|
||||
"version": "2.9.8",
|
||||
"version": "2.9.9",
|
||||
"author": "lingdocs.com",
|
||||
"description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations",
|
||||
"homepage": "https://verbs.lingdocs.com",
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { getShort } from "../../lib/p-text-helpers";
|
||||
import * as T from "../../types";
|
||||
import Examples from "../Examples";
|
||||
import { getShort } from "../lib/p-text-helpers";
|
||||
import * as T from "../types";
|
||||
import Examples from "./Examples";
|
||||
|
||||
function EPTextDisplay({ compiled, opts, justify, onlyOne }: {
|
||||
function CompiledPTextDisplay({ compiled, opts, justify, onlyOne }: {
|
||||
compiled: {
|
||||
ps: T.SingleOrLengthOpts<T.PsString[]>;
|
||||
e?: string[] | undefined;
|
||||
|
@ -30,4 +30,4 @@ function EPTextDisplay({ compiled, opts, justify, onlyOne }: {
|
|||
</div>;
|
||||
}
|
||||
|
||||
export default EPTextDisplay;
|
||||
export default CompiledPTextDisplay;
|
|
@ -1,405 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2021 lingdocs.com
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
|
||||
import { useEffect, useReducer } from "react";
|
||||
import VerbInfo from "./verb-info/VerbInfo";
|
||||
import VerbFormDisplay from "./VerbFormDisplay";
|
||||
import ButtonSelect from "./ButtonSelect";
|
||||
import Hider from "./Hider";
|
||||
import { getForms } from "../lib/conjugation-forms";
|
||||
import { conjugateVerb } from "../lib/verb-conjugation";
|
||||
import PersonSelection from "./PersonSelection";
|
||||
import {
|
||||
incrementPerson,
|
||||
parseEc,
|
||||
} from "../lib/misc-helpers";
|
||||
import * as T from "../types";
|
||||
import {
|
||||
randomPerson,
|
||||
isInvalidSubjObjCombo,
|
||||
} from "../lib/np-tools";
|
||||
|
||||
const VerbChoiceWarning = () => (
|
||||
<>
|
||||
<div className="alert alert-warning d-block mx-auto mt-3" role="alert" style={{ maxWidth: "500px" }}>
|
||||
<i className="fas fa-exclamation-triangle" /> This verb can be used in different ways!
|
||||
</div>
|
||||
<p>Choose which way to use it:</p>
|
||||
</>
|
||||
);
|
||||
|
||||
const stateLocalStorageName = "explorerState6";
|
||||
|
||||
// remember to increment the stateLocalStorageName whenever changing
|
||||
// the State type
|
||||
type State = {
|
||||
mode: "chart" | "sentence";
|
||||
subject: T.Person,
|
||||
object: T.Person,
|
||||
negative: boolean,
|
||||
compoundTypeSelected: "stative" | "dynamic";
|
||||
transitivitySelected: "transitive" | "grammatically transitive";
|
||||
compoundComplementVersionSelected: "sing" | "plur";
|
||||
showingStemsAndRoots: boolean;
|
||||
formsOpened: string[];
|
||||
showingFormInfo: boolean;
|
||||
}
|
||||
|
||||
type Action = {
|
||||
type: "choose compound type",
|
||||
payload: "stative" | "dynamic",
|
||||
} | {
|
||||
type: "set forms opened",
|
||||
payload: string,
|
||||
} | {
|
||||
type: "set compound complement version",
|
||||
payload: "sing" | "plur",
|
||||
} | {
|
||||
type: "toggle showingStemsAndRoots",
|
||||
} | {
|
||||
type: "setState",
|
||||
payload: State,
|
||||
} | {
|
||||
type: "setMode",
|
||||
payload: "chart" | "sentence",
|
||||
} | {
|
||||
type: "setPerson",
|
||||
payload: { setting: "subject" | "object", person: T.Person },
|
||||
} | {
|
||||
type: "randomPerson",
|
||||
payload: "subject" | "object",
|
||||
} | {
|
||||
type: "setShowingFormInfo",
|
||||
payload: boolean,
|
||||
} | {
|
||||
type: "setTransitivitySelected",
|
||||
payload: "transitive" | "grammatically transitive",
|
||||
} | {
|
||||
type: "setNegative",
|
||||
payload: boolean,
|
||||
};
|
||||
|
||||
function oppositeRole(x: "subject" | "object"): "subject" | "object" {
|
||||
return x === "subject" ? "object" : "subject";
|
||||
}
|
||||
|
||||
function reducer(state: State, action: Action): State {
|
||||
function applyFormOpen(
|
||||
payload: string,
|
||||
formsOpened: string[],
|
||||
): string[] {
|
||||
if (formsOpened.includes(payload)) {
|
||||
return formsOpened.filter((f) => f !== payload);
|
||||
}
|
||||
return [...formsOpened, payload];
|
||||
}
|
||||
function setPerson({ setting, person }: { setting: "subject" | "object", person: T.Person }): State {
|
||||
let newPerson = person;
|
||||
let otherPerson = state[oppositeRole(setting)];
|
||||
let otherSetting = oppositeRole(setting);
|
||||
while (isInvalidSubjObjCombo(newPerson, otherPerson)) {
|
||||
otherPerson = incrementPerson(otherPerson);
|
||||
}
|
||||
return { ...state, [setting]: newPerson, [otherSetting]: otherPerson };
|
||||
}
|
||||
|
||||
switch(action.type) {
|
||||
case "choose compound type":
|
||||
return { ...state, compoundTypeSelected: action.payload };
|
||||
case "set forms opened":
|
||||
return {
|
||||
...state,
|
||||
formsOpened: applyFormOpen(action.payload, state.formsOpened),
|
||||
};
|
||||
case "set compound complement version":
|
||||
return { ...state, compoundComplementVersionSelected: action.payload };
|
||||
case "toggle showingStemsAndRoots":
|
||||
return { ...state, showingStemsAndRoots: !state.showingStemsAndRoots };
|
||||
case "setState":
|
||||
return action.payload;
|
||||
case "setMode":
|
||||
return { ...state, mode: action.payload };
|
||||
case "setPerson":
|
||||
return setPerson(action.payload);
|
||||
case "randomPerson":
|
||||
return {
|
||||
...state,
|
||||
[action.payload]: randomPerson({
|
||||
prev: state[action.payload === "subject" ? "object" : "subject"]
|
||||
}),
|
||||
};
|
||||
case "setShowingFormInfo":
|
||||
return {
|
||||
...state,
|
||||
showingFormInfo: action.payload,
|
||||
};
|
||||
case "setTransitivitySelected":
|
||||
return {
|
||||
...state,
|
||||
transitivitySelected: action.payload,
|
||||
};
|
||||
case "setNegative":
|
||||
return {
|
||||
...state,
|
||||
negative: action.payload,
|
||||
};
|
||||
default:
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
const initialState: State = {
|
||||
subject: 0,
|
||||
object: 2,
|
||||
negative: false,
|
||||
compoundTypeSelected: "stative",
|
||||
transitivitySelected: "transitive",
|
||||
mode: "chart",
|
||||
compoundComplementVersionSelected: "plur",
|
||||
showingStemsAndRoots: false,
|
||||
showingFormInfo: false,
|
||||
formsOpened: [],
|
||||
};
|
||||
|
||||
function ConjugationViewer({ entry, complement, textOptions, showOnly, highlightInRootsAndStems, hidePastParticiple, sentenceLevel }: {
|
||||
entry: T.DictionaryEntry,
|
||||
complement?: T.DictionaryEntry,
|
||||
textOptions: T.TextOptions,
|
||||
showOnly?: string | string[],
|
||||
highlightInRootsAndStems?: T.RootsOrStemsToHighlight,
|
||||
hidePastParticiple?: boolean,
|
||||
sentenceLevel?: "easy" | "medium" | "hard",
|
||||
}) {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
useEffect(() => {
|
||||
const stateRaw = localStorage.getItem(stateLocalStorageName);
|
||||
if (stateRaw) {
|
||||
try {
|
||||
const payload = JSON.parse(stateRaw) as State;
|
||||
dispatch({ type: "setState", payload });
|
||||
} catch (e) {
|
||||
console.error("error parsing saved state JSON", e);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
localStorage.setItem(stateLocalStorageName, JSON.stringify(state));
|
||||
});
|
||||
const conjugation = (() => {
|
||||
if (!(entry.c && entry.c.slice(0, 2) === "v.")) return undefined;
|
||||
try {
|
||||
return conjugateVerb(entry, complement);
|
||||
} catch(e) {
|
||||
console.error("conjugation error", e);
|
||||
return undefined;
|
||||
}
|
||||
})();
|
||||
if (conjugation === undefined) {
|
||||
// don't show the conjugation viewer if the verb can't be conjugated
|
||||
return null;
|
||||
}
|
||||
|
||||
const verbConj1 = ("dynamic" in conjugation)
|
||||
? conjugation[state.compoundTypeSelected]
|
||||
: ("transitive" in conjugation)
|
||||
? conjugation[state.transitivitySelected === "transitive" ? "transitive" : "grammaticallyTransitive"]
|
||||
: conjugation;
|
||||
const verbConj = (verbConj1.singularForm && state.compoundComplementVersionSelected === "sing")
|
||||
? verbConj1.singularForm
|
||||
: verbConj1;
|
||||
const englishConjugation: T.EnglishVerbConjugation | undefined = entry.ec ? {
|
||||
ec: parseEc(entry.ec),
|
||||
ep: entry.ep,
|
||||
} : undefined;
|
||||
|
||||
const limitTo = !showOnly
|
||||
? undefined
|
||||
: Array.isArray(showOnly)
|
||||
? showOnly
|
||||
: [showOnly];
|
||||
const forms = getForms({
|
||||
conj: verbConj,
|
||||
filterFunc: [
|
||||
...limitTo ? [(f: T.DisplayForm): boolean => limitTo.includes(f.label)
|
||||
] : [],
|
||||
],
|
||||
mode: state.mode,
|
||||
subject: state.subject,
|
||||
object: state.object,
|
||||
negative: state.negative,
|
||||
sentenceLevel,
|
||||
englishConjugation,
|
||||
});
|
||||
const sentencesAvailable = forms.some((form) => "sentence" in form);
|
||||
return <div className="mb-4">
|
||||
{"transitive" in conjugation && <div className="text-center my-2">
|
||||
<VerbChoiceWarning />
|
||||
<div>
|
||||
<ButtonSelect
|
||||
options={[
|
||||
{
|
||||
label: "Transitive", value: "transitive" },
|
||||
{
|
||||
label: "Grammatically Transitive",
|
||||
value: "grammatically transitive",
|
||||
},
|
||||
]}
|
||||
value={state.transitivitySelected}
|
||||
handleChange={(p) => dispatch({ type: "setTransitivitySelected", payload: p })}
|
||||
/>
|
||||
</div>
|
||||
</div>}
|
||||
{"dynamic" in conjugation && <div className="text-center my-2">
|
||||
<VerbChoiceWarning />
|
||||
<div>
|
||||
<ButtonSelect
|
||||
options={[
|
||||
{ label: "Dynamic", value: "dynamic" },
|
||||
{
|
||||
label: `${conjugation.info.type === "dynamic or generative stative compound" ? "Generative " : ""}Stative`,
|
||||
value: "stative",
|
||||
},
|
||||
]}
|
||||
value={state.compoundTypeSelected}
|
||||
handleChange={(p) => dispatch({ type: "choose compound type", payload: p })}
|
||||
/>
|
||||
</div>
|
||||
</div>}
|
||||
{verbConj1.singularForm && <div className="text-center my-2">
|
||||
<VerbChoiceWarning />
|
||||
<div>
|
||||
<ButtonSelect
|
||||
small
|
||||
options={[
|
||||
// @ts-ignore - TODO - make this a bit safer
|
||||
{ label: `Sing. ${verbConj1.info.objComplement.entry.p}`, value: "sing" },
|
||||
// @ts-ignore - TODO - make this a bit safer
|
||||
{ label: `Plur. ${verbConj1.info.objComplement.plural.p}`, value: "plur" },
|
||||
]}
|
||||
value={state.compoundComplementVersionSelected}
|
||||
handleChange={(p) => dispatch({ type: "set compound complement version", payload: p })}
|
||||
/>
|
||||
</div>
|
||||
</div>}
|
||||
<VerbInfo
|
||||
info={verbConj.info}
|
||||
textOptions={textOptions}
|
||||
showingStemsAndRoots={state.showingStemsAndRoots}
|
||||
highlightInRootsAndStems={highlightInRootsAndStems}
|
||||
toggleShowingSar={() => dispatch({ type: "toggle showingStemsAndRoots" })}
|
||||
hidePastParticiple={hidePastParticiple}
|
||||
hideTypeInfo={!!limitTo}
|
||||
/>
|
||||
<div className="d-flex flex-row align-items-center justify-content-around flex-wrap mt-4 mb-2">
|
||||
{sentencesAvailable && <div className="mb-3">
|
||||
<ButtonSelect
|
||||
options={[
|
||||
{ label: `Chart${forms.length !== 1 ? "s" : ""}`, value: "chart" },
|
||||
{ label: `Sentences`, value: "sentence" },
|
||||
]}
|
||||
value={state.mode}
|
||||
handleChange={(p) => dispatch({ type: "setMode", payload: p })}
|
||||
/>
|
||||
</div>}
|
||||
{!limitTo && <>
|
||||
<div className="form-group form-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="form-check-input"
|
||||
checked={state.showingFormInfo}
|
||||
onChange={(e) => {
|
||||
dispatch({ type: "setShowingFormInfo", payload: e.target.checked })
|
||||
}}
|
||||
/>
|
||||
<label className="form-check-label">Show Form Info</label>
|
||||
</div>
|
||||
</>}
|
||||
</div>
|
||||
{(state.mode === "sentence" && sentencesAvailable) &&
|
||||
<div className="position-sticky pb-1" style={{ top: 0, background: "var(--theme-shade)", zIndex: 1000 }}>
|
||||
<PersonSelection
|
||||
subject={state.subject}
|
||||
object={state.object}
|
||||
info={verbConj.info}
|
||||
handleChange={(payload: { setting: "subject" | "object", person: T.Person }) => dispatch({
|
||||
type: "setPerson", payload,
|
||||
})}
|
||||
handleRandom={(payload: "subject" | "object") => dispatch({
|
||||
type: "randomPerson", payload,
|
||||
})}
|
||||
textOptions={textOptions}
|
||||
/>
|
||||
<div className="mt-2 text-center">
|
||||
<ButtonSelect
|
||||
options={[
|
||||
{ label: "Pos.", value: "false" },
|
||||
{ label: "Neg.", value: "true" },
|
||||
]}
|
||||
value={state.negative.toString()}
|
||||
handleChange={(p) => dispatch({ type: "setNegative", payload: p === "true" })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<FormsDisplay
|
||||
forms={forms}
|
||||
state={{ ...state, mode: sentencesAvailable ? "chart" : state.mode }}
|
||||
handleChange={(payload: string) => dispatch({ type: "set forms opened", payload })}
|
||||
verbConj={verbConj}
|
||||
textOptions={textOptions}
|
||||
/>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function FormsDisplay({ forms, state, handleChange, textOptions, verbConj }: {
|
||||
verbConj: T.VerbConjugation,
|
||||
forms: T.DisplayFormItem[],
|
||||
state: State,
|
||||
handleChange: (p: string) => void,
|
||||
textOptions: T.TextOptions,
|
||||
}) {
|
||||
function drawLevel(forms: T.DisplayFormItem[], level: number) {
|
||||
return <div className="mt-3">
|
||||
{forms.map((f, i) => {
|
||||
if ("content" in f && f.content.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const showDividerLine = (item: T.DisplayFormItem, index: number): boolean => {
|
||||
return (index !== 0) && ("content" in item || !item.aspect || (item.aspect === "imperfective"));
|
||||
};
|
||||
return <div key={`level1-${i}`}>
|
||||
{showDividerLine(f, i) && <hr />}
|
||||
<Hider
|
||||
label={f.label}
|
||||
hLevel={5+level}
|
||||
aspect={"aspect" in f ? f.aspect : undefined}
|
||||
showing={state.formsOpened.includes(f.label)}
|
||||
handleChange={() => handleChange(f.label)}
|
||||
ignore={forms.length === 1}
|
||||
>
|
||||
{"content" in f ?
|
||||
drawLevel(f.content, level + 1)
|
||||
:
|
||||
<VerbFormDisplay
|
||||
displayForm={f}
|
||||
textOptions={textOptions}
|
||||
showingFormInfo={state.showingFormInfo}
|
||||
info={verbConj.info}
|
||||
/>
|
||||
}
|
||||
</Hider>
|
||||
</div>;
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
return <div className="mb-2">
|
||||
{drawLevel(forms, 0)}
|
||||
</div>;
|
||||
}
|
||||
|
||||
export default ConjugationViewer;
|
|
@ -0,0 +1,23 @@
|
|||
export type Mode = "text" | "blocks";
|
||||
|
||||
function ModeSelect({ value, onChange }: { value: Mode, onChange: (m: Mode) => void }) {
|
||||
return <div style={{ fontSize: "larger", maxWidth: "1.75rem" }}>
|
||||
{value === "text" ? <div className="clickable" onClick={() => onChange("blocks")}>
|
||||
<i className="fas fa-cubes" />
|
||||
</div> : <div className="clickable" onClick={() => onChange("text")}>
|
||||
<i className="fas fa-align-left" />
|
||||
</div>}
|
||||
</div>;
|
||||
}
|
||||
|
||||
export function ScriptSelect({ value, onChange }: { value: "p" | "f", onChange: (m: "p" | "f") => void }) {
|
||||
return <button style={{ marginLeft: "1.5rem" }} className="btn btn-sm btn-light">
|
||||
{value === "p" ? <div className="clickable" onClick={() => onChange("f")}>
|
||||
Ps
|
||||
</div> : <div className="clickable" onClick={() => onChange("p")}>
|
||||
Phon.
|
||||
</div>}
|
||||
</button>;
|
||||
}
|
||||
|
||||
export default ModeSelect;
|
|
@ -0,0 +1,55 @@
|
|||
import { useState } from "react";
|
||||
import { filterForVisibleBlocksEP, filterForVisibleBlocksVP } from "../lib/phrase-building/compile";
|
||||
import * as T from "../types";
|
||||
import Block from "./blocks/Block";
|
||||
import KidDisplay from "./blocks/KidDisplay";
|
||||
|
||||
function RenderedBlocksDisplay({ opts, rendered, justify, script }: {
|
||||
script: "p" | "f",
|
||||
opts: T.TextOptions,
|
||||
rendered: T.EPRendered | T.VPRendered,
|
||||
justify?: "left" | "right" | "center",
|
||||
}) {
|
||||
const [variation, setVariation] = useState<number>(0);
|
||||
const blocksWVars = ("omitSubject" in rendered)
|
||||
? filterForVisibleBlocksEP(rendered.blocks, rendered.omitSubject)
|
||||
: filterForVisibleBlocksVP(rendered.blocks, rendered.form, rendered.king);
|
||||
const king = "king" in rendered ? rendered.king : undefined;
|
||||
const blocks = blocksWVars[variation];
|
||||
function handleVariationChange() {
|
||||
setVariation(ov => ((ov + 1) % blocksWVars.length));
|
||||
}
|
||||
return <div className={`d-flex flex-row justify-content-${justify ? justify : "center"}`} style={{}}>
|
||||
<div className={`d-flex flex-row${script === "p" ? "-reverse" : ""} justify-content-left align-items-end mt-3 pb-2`} style={{ overflowX: "auto" }}>
|
||||
<div key={Math.random()} className="mr-2">
|
||||
<Block opts={opts} block={blocks[0]} king={king} script={script} />
|
||||
</div>
|
||||
<KidsSection opts={opts} kids={rendered.kids} script={script} />
|
||||
{blocks.slice(1).map((block) => (
|
||||
<div key={Math.random()} className="mr-2">
|
||||
<Block opts={opts} block={block} king={king} script={script} />
|
||||
</div>
|
||||
))}
|
||||
<div style={{ height: "100%" }} className="d-flex flex-column justify-content-center">
|
||||
{blocksWVars.length > 1 && <button onClick={handleVariationChange} className="btn btn-light btn-sm mx-2">V. {variation + 1}/{blocksWVars.length}</button>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
function KidsSection({ opts, kids, script }: {
|
||||
opts: T.TextOptions,
|
||||
kids: T.Kid[],
|
||||
script: "p" | "f",
|
||||
}) {
|
||||
return kids.length > 0 ? <div className="text-center mx-1 mr-3" style={{ paddingBottom: "1rem"}}>
|
||||
<div className={`d-flex flex-row${script === "p" ? "-reverse" : ""} mb-3 justify-content-center`}>
|
||||
{kids.map(kid => (
|
||||
<KidDisplay key={Math.random()} opts={opts} kid={kid} script={script} />
|
||||
))}
|
||||
</div>
|
||||
<div><strong>kids</strong></div>
|
||||
</div> : null;
|
||||
}
|
||||
|
||||
export default RenderedBlocksDisplay;
|
|
@ -4,89 +4,245 @@ import {
|
|||
getEnglishFromRendered,
|
||||
} from "../../lib/phrase-building/np-tools";
|
||||
import { getEnglishPersonInfo } from "../../library";
|
||||
import { useState } from "react";
|
||||
import { getLength } from "../../lib/p-text-helpers";
|
||||
import { roleIcon } from "../vp-explorer/VPExplorerExplanationModal";
|
||||
import { negativeParticle } from "../../lib/grammar-units";
|
||||
|
||||
function Block({ opts, block }: {
|
||||
function Block({ opts, block, king, script }: {
|
||||
opts: T.TextOptions,
|
||||
block: T.Block,
|
||||
king?: "subject" | "object" | undefined,
|
||||
script: "p" | "f";
|
||||
}) {
|
||||
if ("equative" in block) {
|
||||
return <EquativeBlock opts={opts} eq={block.equative} />;
|
||||
return <EquativeBlock opts={opts} eq={block.equative} script={script} />;
|
||||
}
|
||||
if (block.type === "AP") {
|
||||
const english = getEnglishFromRendered(block);
|
||||
return <APBlock opts={opts} english={english}>{block}</APBlock>
|
||||
return <APBlock opts={opts} english={english} script={script}>{block}</APBlock>
|
||||
}
|
||||
if (block.type === "subjectSelection") {
|
||||
return <SubjectBlock opts={opts} np={block.selection} />
|
||||
const role = king === "subject" ? "king" : king === "object" ? "servant" : undefined;
|
||||
return <SubjectBlock opts={opts} np={block.selection} role={role} script={script} />
|
||||
}
|
||||
if (block.type === "predicateSelection") {
|
||||
const english = getEnglishFromRendered(block.selection);
|
||||
return <div className="text-center">
|
||||
<div><strong>Predicate</strong></div>
|
||||
{block.selection.type === "EQComp"
|
||||
? <EqCompBlock opts={opts} comp={block.selection.selection} />
|
||||
: <NPBlock opts={opts} english={english}>{block.selection}</NPBlock>}
|
||||
? <EqCompBlock opts={opts} comp={block.selection.selection} script={script} />
|
||||
: <NPBlock opts={opts} english={english} script={script}>{block.selection}</NPBlock>}
|
||||
</div>
|
||||
}
|
||||
if (block.type === "nu") {
|
||||
return <NUBlock opts={opts} />
|
||||
if (block.type === "negative") {
|
||||
return <NegBlock opts={opts} imperative={block.imperative} script={script} />
|
||||
}
|
||||
if (block.type === "perfectiveHead") {
|
||||
return <PerfHeadBlock opts={opts} ps={block.ps} script={script} />
|
||||
}
|
||||
if (block.type === "verbComplement") {
|
||||
return <VCompBlock opts={opts} comp={block.complement} script={script} />;
|
||||
}
|
||||
if (block.type === "verb") {
|
||||
return <VerbSBlock opts={opts} v={block.block} script={script} />;
|
||||
}
|
||||
if (block.type === "objectSelection") {
|
||||
const role = king === "object" ? "king" : king === "subject" ? "servant" : undefined;
|
||||
return <ObjectBlock opts={opts} obj={block.selection} role={role} script={script} />;
|
||||
}
|
||||
if (block.type === "perfectParticipleBlock") {
|
||||
return <VerbSBlock opts={opts} v={block} script={script} />;
|
||||
}
|
||||
if (block.type === "perfectEquativeBlock") {
|
||||
return <EquativeBlock opts={opts} eq={block} script={script} />;
|
||||
}
|
||||
if (block.type === "modalVerbBlock") {
|
||||
return <ModalVerbBlock opts={opts} v={block} script={script} />;
|
||||
}
|
||||
if (block.type === "modalVerbKedulPart") {
|
||||
return <ModalAuxBlock opts={opts} aux={block} script={script} />
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export default Block;
|
||||
|
||||
function NUBlock({ opts }: {
|
||||
opts: T.TextOptions,
|
||||
}) {
|
||||
return <div className="text-center">
|
||||
<div
|
||||
className={classNames("d-flex flex-row justify-content-center align-items-center")}
|
||||
function Border({ children, extraClassName, padding }: { children: JSX.Element | JSX.Element[] | string, extraClassName?: string, padding?: string }) {
|
||||
return <div
|
||||
className={`d-flex flex-row justify-content-center align-items-center ${extraClassName ? extraClassName : ""}`}
|
||||
style={{
|
||||
border: "2px solid black",
|
||||
padding: "1rem",
|
||||
padding: padding ? padding : "1rem",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
nu
|
||||
<>{children}</>
|
||||
</div>
|
||||
}
|
||||
|
||||
function VerbSBlock({ opts, v, script }: {
|
||||
opts: T.TextOptions,
|
||||
script: "p" | "f",
|
||||
v: T.VerbRenderedBlock["block"] | T.PerfectParticipleBlock,
|
||||
}) {
|
||||
const [length, setLength] = useState<T.Length>("long");
|
||||
function changeLength() {
|
||||
setLength(o => (
|
||||
o === "long"
|
||||
? "short"
|
||||
: o === "short" && "mini" in v.ps
|
||||
? "mini"
|
||||
: "long"
|
||||
));
|
||||
}
|
||||
return <div className="text-center">
|
||||
{"long" in v.ps && <div className="clickable small mb-1" onClick={changeLength}>{length}</div>}
|
||||
<Border>
|
||||
{getLength(v.ps, length)[0][script]}
|
||||
</Border>
|
||||
<div>{v.type === "perfectParticipleBlock" ? "Past Partic." : "Verb"}</div>
|
||||
<EnglishBelow>{getEnglishPersonInfo(v.person, "short")}</EnglishBelow>
|
||||
</div>
|
||||
}
|
||||
|
||||
function ModalVerbBlock({ opts, v, script }: {
|
||||
opts: T.TextOptions,
|
||||
script: "p" | "f",
|
||||
v: T.ModalVerbBlock,
|
||||
}) {
|
||||
const [length, setLength] = useState<T.Length>("long");
|
||||
function changeLength() {
|
||||
setLength(o => (
|
||||
o === "long"
|
||||
? "short"
|
||||
: "long"
|
||||
));
|
||||
}
|
||||
return <div className="text-center">
|
||||
{"long" in v.ps && <div className="clickable small mb-1" onClick={changeLength}>{length}</div>}
|
||||
<Border>
|
||||
{getLength(v.ps, length)[0][script]}
|
||||
</Border>
|
||||
<div>Verb</div>
|
||||
<EnglishBelow>Modal</EnglishBelow>
|
||||
</div>
|
||||
}
|
||||
|
||||
function PerfHeadBlock({ opts, ps, script }: {
|
||||
opts: T.TextOptions,
|
||||
ps: T.PsString,
|
||||
script: "p" | "f",
|
||||
|
||||
}) {
|
||||
return <div className="text-center">
|
||||
<Border>
|
||||
{ps[script]}
|
||||
</Border>
|
||||
<div>perf. head</div>
|
||||
<EnglishBelow>{'\u00A0'}</EnglishBelow>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function VCompBlock({ opts, comp, script }: {
|
||||
opts: T.TextOptions,
|
||||
comp: T.VerbComplementBlock["complement"],
|
||||
script: "p" | "f",
|
||||
}) {
|
||||
return <div className="text-center">
|
||||
<Border>
|
||||
{comp[script]}
|
||||
</Border>
|
||||
<div>Complement</div>
|
||||
<EnglishBelow>{'\u00A0'}</EnglishBelow>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function ModalAuxBlock({ opts, aux, script }: {
|
||||
opts: T.TextOptions,
|
||||
aux: T.ModalVerbKedulPart,
|
||||
script: "p" | "f",
|
||||
|
||||
}) {
|
||||
return <div className="text-center">
|
||||
<Border>
|
||||
{aux.ps[0][script]}
|
||||
</Border>
|
||||
<div>Modal Aux</div>
|
||||
<EnglishBelow>{getEnglishPersonInfo(aux.verb.block.person, "short")}</EnglishBelow>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function NegBlock({ opts, imperative, script }: {
|
||||
opts: T.TextOptions,
|
||||
imperative: boolean,
|
||||
script: "p" | "f",
|
||||
}) {
|
||||
return <div className="text-center">
|
||||
<Border>
|
||||
{negativeParticle[imperative ? "imperative" : "nonImperative"][script]}
|
||||
</Border>
|
||||
<div>Neg.</div>
|
||||
<EnglishBelow>not</EnglishBelow>
|
||||
<EnglishBelow>{imperative ? "don't" : "not"}</EnglishBelow>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function EquativeBlock({ opts, eq }: {
|
||||
function EquativeBlock({ opts, eq, script }: {
|
||||
opts: T.TextOptions,
|
||||
eq: T.EquativeRendered,
|
||||
eq: T.EquativeRendered | T.PerfectEquativeBlock,
|
||||
script: "p" | "f",
|
||||
}) {
|
||||
const [length, setLength] = useState<T.Length>("long");
|
||||
function changeLength() {
|
||||
setLength(o => (
|
||||
o === "long"
|
||||
? "short"
|
||||
: o === "short" && "mini" in eq.ps
|
||||
? "mini"
|
||||
: "long"
|
||||
));
|
||||
}
|
||||
return <div className="text-center">
|
||||
<div
|
||||
className={classNames("d-flex flex-row justify-content-center align-items-center")}
|
||||
style={{
|
||||
border: "2px solid black",
|
||||
padding: "1rem",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
{"short" in eq.ps ? eq.ps.short[0].f : eq.ps[0].f}
|
||||
</div>
|
||||
{"long" in eq.ps && <div className="clickable small mb-1" onClick={changeLength}>{length}</div>}
|
||||
<Border>
|
||||
{getLength(eq.ps, length)[0][script]}
|
||||
</Border>
|
||||
<div>Equative</div>
|
||||
<EnglishBelow>{"="}</EnglishBelow>
|
||||
<EnglishBelow>{getEnglishPersonInfo(eq.person, "short")}</EnglishBelow>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function SubjectBlock({ opts, np }: {
|
||||
function SubjectBlock({ opts, np, role, script }: {
|
||||
opts: T.TextOptions,
|
||||
np: T.Rendered<T.NPSelection>,
|
||||
role: "king" | "servant" | undefined,
|
||||
script: "p" | "f",
|
||||
}) {
|
||||
const english = getEnglishFromRendered(np);
|
||||
return <div className="text-center">
|
||||
<div><strong>Subject</strong></div>
|
||||
<NPBlock opts={opts} english={english}>{np}</NPBlock>
|
||||
<div><strong>Subject</strong>{role ? roleIcon[role] : ""}</div>
|
||||
<NPBlock opts={opts} english={english} script={script}>{np}</NPBlock>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function EqCompBlock({ opts, comp }: {
|
||||
function ObjectBlock({ opts, obj, role, script }: {
|
||||
opts: T.TextOptions,
|
||||
obj: T.Rendered<T.ObjectSelectionComplete>["selection"],
|
||||
role: "king" | "servant" | undefined,
|
||||
script: "p" | "f",
|
||||
}) {
|
||||
if (typeof obj !== "object") {
|
||||
return null;
|
||||
}
|
||||
const english = getEnglishFromRendered(obj);
|
||||
return <div className="text-center">
|
||||
<div><strong>Object</strong>{role ? roleIcon[role] : ""}</div>
|
||||
<NPBlock opts={opts} english={english} script={script}>{obj}</NPBlock>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function EqCompBlock({ opts, comp, script }: {
|
||||
script: "p" | "f",
|
||||
opts: T.TextOptions,
|
||||
comp: T.Rendered<T.EqCompSelection["selection"]>,
|
||||
}) {
|
||||
|
@ -95,16 +251,9 @@ function EqCompBlock({ opts, comp }: {
|
|||
adj: T.Rendered<T.AdjectiveSelection>,
|
||||
}) {
|
||||
return <div className="text-center">
|
||||
<div
|
||||
className={classNames("d-flex flex-row justify-content-center align-items-center")}
|
||||
style={{
|
||||
border: "2px solid black",
|
||||
padding: "1rem",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
{adj.ps[0].f}
|
||||
</div>
|
||||
<Border>
|
||||
{adj.ps[0][script]}
|
||||
</Border>
|
||||
<div>Adj.</div>
|
||||
<EnglishBelow>{adj.e}</EnglishBelow>
|
||||
</div>;
|
||||
|
@ -115,16 +264,9 @@ function EqCompBlock({ opts, comp }: {
|
|||
adv: T.Rendered<T.LocativeAdverbSelection>,
|
||||
}) {
|
||||
return <div className="text-center">
|
||||
<div
|
||||
className={classNames("d-flex flex-row justify-content-center align-items-center")}
|
||||
style={{
|
||||
border: "2px solid black",
|
||||
padding: "1rem",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
{adv.ps[0].f}
|
||||
</div>
|
||||
<Border>
|
||||
{adv.ps[0][script]}
|
||||
</Border>
|
||||
<div>Loc. Adv.</div>
|
||||
<EnglishBelow>{adv.e}</EnglishBelow>
|
||||
</div>;
|
||||
|
@ -137,89 +279,78 @@ function EqCompBlock({ opts, comp }: {
|
|||
: comp.type === "loc. adv."
|
||||
? <LocAdvBlock opts={opts} adv={comp} />
|
||||
: <div>
|
||||
<Sandwich opts={opts} sandwich={comp} />
|
||||
<Sandwich opts={opts} sandwich={comp} script={script} />
|
||||
<div>Sandwich</div>
|
||||
<EnglishBelow>{comp.e}</EnglishBelow>
|
||||
</div>}
|
||||
</div>;
|
||||
}
|
||||
|
||||
export function APBlock({ opts, children, english }: {
|
||||
export function APBlock({ opts, children, english, script }: {
|
||||
opts: T.TextOptions,
|
||||
children: T.Rendered<T.APSelection>,
|
||||
english?: string,
|
||||
script: "p" | "f",
|
||||
}) {
|
||||
const ap = children;
|
||||
if (ap.selection.type === "adverb") {
|
||||
return <div className="text-center">
|
||||
<div
|
||||
className={classNames("d-flex flex-row justify-content-center align-items-center")}
|
||||
style={{
|
||||
border: "2px solid black",
|
||||
padding: "1rem",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
{ap.selection.ps[0].f}
|
||||
</div>
|
||||
<Border>
|
||||
{ap.selection.ps[0][script]}
|
||||
</Border>
|
||||
<div>AP</div>
|
||||
<EnglishBelow>{english}</EnglishBelow>
|
||||
</div>;
|
||||
}
|
||||
return <div>
|
||||
<Sandwich opts={opts} sandwich={ap.selection} />
|
||||
<Sandwich opts={opts} sandwich={ap.selection} script={script} />
|
||||
<div>AP</div>
|
||||
<EnglishBelow>{english}</EnglishBelow>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function Sandwich({ opts, sandwich }: {
|
||||
function Sandwich({ opts, sandwich, script }: {
|
||||
opts: T.TextOptions,
|
||||
sandwich: T.Rendered<T.SandwichSelection<T.Sandwich>>,
|
||||
script: "p" | "f",
|
||||
}) {
|
||||
return <div className="text-center">
|
||||
<div className="text-center">Sandwich 🥪</div>
|
||||
<div
|
||||
className={classNames("d-flex flex-row justify-content-center align-items-center")}
|
||||
style={{
|
||||
border: "2px solid black",
|
||||
padding: "0.75rem 0.5rem 0.25rem 0.5rem",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<div className="d-flex flex-row justify-content-between align-items-end">
|
||||
<Possesors opts={opts}>{sandwich.inside.selection.type !== "pronoun" ? sandwich.inside.selection.possesor : undefined}</Possesors>
|
||||
<Border padding="0.75rem 0.5rem 0.25rem 0.5rem">
|
||||
<div className={`d-flex flex-row${script === "p" ? "-reverse" : ""} justify-content-between align-items-end`}>
|
||||
<Possesors opts={opts} script={script}>{sandwich.inside.selection.type !== "pronoun" ? sandwich.inside.selection.possesor : undefined}</Possesors>
|
||||
<div className="mr-2 ml-1 mb-1"><strong>{sandwich.before ? sandwich.before.f : ""}</strong></div>
|
||||
<div>
|
||||
<NPBlock opts={opts} inside>{sandwich.inside}</NPBlock>
|
||||
<NPBlock opts={opts} inside script={script}>{sandwich.inside}</NPBlock>
|
||||
</div>
|
||||
<div className="ml-2 mr-1 mb-1"><strong>{sandwich.after ? sandwich.after.f : ""}</strong></div>
|
||||
</div>
|
||||
</div>
|
||||
</Border>
|
||||
</div>;
|
||||
}
|
||||
|
||||
export function NPBlock({ opts, children, inside, english }: {
|
||||
export function NPBlock({ opts, children, inside, english, script }: {
|
||||
opts: T.TextOptions,
|
||||
children: T.Rendered<T.NPSelection>,
|
||||
inside?: boolean,
|
||||
english?: string,
|
||||
script: "p" | "f",
|
||||
}) {
|
||||
const np = children;
|
||||
const hasPossesor = !!(np.selection.type !== "pronoun" && np.selection.possesor && !np.selection.possesor.shrunken);
|
||||
const elements = [
|
||||
...!inside ? [<Possesors opts={opts} script={script}>{np.selection.type !== "pronoun" ? np.selection.possesor : undefined}</Possesors>] : [],
|
||||
<Adjectives opts={opts} script={script}>{np.selection.adjectives}</Adjectives>,
|
||||
<div className={np.selection.adjectives?.length ? "mx-1" : ""}> {np.selection.ps[0][script]}</div>,
|
||||
];
|
||||
const el = script === "p" ? elements.reverse() : elements;
|
||||
return <div className="text-center">
|
||||
<div
|
||||
className={classNames("d-flex flex-row justify-content-center align-items-center", { "pt-2": !inside && hasPossesor })}
|
||||
style={{
|
||||
border: "2px solid black",
|
||||
padding: inside ? "0.3rem" : hasPossesor ? "0.5rem 1rem 0.25rem 1rem" : "1rem",
|
||||
textAlign: "center",
|
||||
}}
|
||||
<Border
|
||||
extraClassName={`!inside && hasPossesor ? "pt-2" : ""`}
|
||||
padding={inside ? "0.3rem" : hasPossesor ? "0.5rem 0.8rem 0.25rem 0.8rem" : "1rem"}
|
||||
>
|
||||
{!inside && <Possesors opts={opts}>{np.selection.type !== "pronoun" ? np.selection.possesor : undefined}</Possesors>}
|
||||
<Adjectives opts={opts}>{np.selection.adjectives}</Adjectives>
|
||||
<div> {np.selection.ps[0].f}</div>
|
||||
</div>
|
||||
{el}
|
||||
</Border>
|
||||
<div className={inside ? "small" : ""}>
|
||||
NP
|
||||
{!inside ? <>
|
||||
|
@ -227,13 +358,14 @@ export function NPBlock({ opts, children, inside, english }: {
|
|||
<span className="text-muted small">({getEnglishPersonInfo(np.selection.person, "short")})</span>
|
||||
</> : <></>}
|
||||
</div>
|
||||
<EnglishBelow>{english}</EnglishBelow>
|
||||
{!inside && <EnglishBelow>{english}</EnglishBelow>}
|
||||
</div>
|
||||
}
|
||||
|
||||
function Possesors({ opts, children }: {
|
||||
function Possesors({ opts, children, script }: {
|
||||
opts: T.TextOptions,
|
||||
children: { shrunken: boolean, np: T.Rendered<T.NPSelection> } | undefined,
|
||||
script: "p" | "f",
|
||||
}) {
|
||||
if (!children) {
|
||||
return null;
|
||||
|
@ -241,18 +373,18 @@ function Possesors({ opts, children }: {
|
|||
if (children.shrunken) {
|
||||
return null;
|
||||
}
|
||||
const contraction = checkForContraction(children.np);
|
||||
return <div className="d-flex flex-row mr-1 align-items-end" style={{
|
||||
const contraction = checkForContraction(children.np, script);
|
||||
return <div className={`d-flex flex-row${script === "p" ? "-reverse" : ""} mr-1 align-items-end`} style={{
|
||||
marginBottom: "0.5rem",
|
||||
borderBottom: "1px solid grey",
|
||||
}}>
|
||||
{children.np.selection.type !== "pronoun" && <Possesors opts={opts}>{children.np.selection.possesor}</Possesors>}
|
||||
{children.np.selection.type !== "pronoun" && <Possesors opts={opts} script={script}>{children.np.selection.possesor}</Possesors>}
|
||||
<div>
|
||||
{contraction && <div className="mb-1">({contraction})</div>}
|
||||
<div className={classNames("d-flex", "flex-row", "align-items-center", { "text-muted": contraction })}>
|
||||
<div className="mr-1 pb-2">du</div>
|
||||
<div className={classNames("d-flex", (script === "f" ? "flex-row" : "flex-row-reverse"), "align-items-center", { "text-muted": contraction })}>
|
||||
<div className="mx-1 pb-2">{script === "p" ? "د" : "du"}</div>
|
||||
<div>
|
||||
<NPBlock opts={opts} inside>{children.np}</NPBlock>
|
||||
<NPBlock script={script} opts={opts} inside>{children.np}</NPBlock>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -260,32 +392,19 @@ function Possesors({ opts, children }: {
|
|||
</div>
|
||||
}
|
||||
|
||||
function checkForContraction(np: T.Rendered<T.NPSelection>): string | undefined {
|
||||
if (np.selection.type !== "pronoun") return undefined;
|
||||
if (np.selection.person === T.Person.FirstSingMale || np.selection.person === T.Person.FirstSingFemale) {
|
||||
return "zmaa"
|
||||
}
|
||||
if (np.selection.person === T.Person.SecondSingMale || np.selection.person === T.Person.SecondSingFemale) {
|
||||
return "staa"
|
||||
}
|
||||
if (np.selection.person === T.Person.FirstPlurMale || np.selection.person === T.Person.FirstPlurFemale) {
|
||||
return "zmoonG"
|
||||
}
|
||||
if (np.selection.person === T.Person.SecondPlurMale || np.selection.person === T.Person.SecondPlurFemale) {
|
||||
return "staaso"
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function Adjectives({ opts, children }: {
|
||||
function Adjectives({ opts, children, script }: {
|
||||
opts: T.TextOptions,
|
||||
children: T.Rendered<T.AdjectiveSelection>[] | undefined,
|
||||
script: "p" | "f",
|
||||
}) {
|
||||
if (!children) {
|
||||
return null;
|
||||
}
|
||||
const c = script === "p"
|
||||
? children.reverse()
|
||||
: children;
|
||||
return <em className="mr-1">
|
||||
{children.map(a => a.ps[0].f).join(" ")}{` `}
|
||||
{c.map(a => a.ps[0][script]).join(" ")}{` `}
|
||||
</em>
|
||||
}
|
||||
|
||||
|
@ -296,3 +415,21 @@ function EnglishBelow({ children: e }: { children: string | undefined }) {
|
|||
height: "1rem",
|
||||
}}>{e ? e : ""}</div>;
|
||||
}
|
||||
|
||||
function checkForContraction(np: T.Rendered<T.NPSelection>, script: "p" | "f"): string | undefined {
|
||||
if (np.selection.type !== "pronoun") return undefined;
|
||||
if (np.selection.person === T.Person.FirstSingMale || np.selection.person === T.Person.FirstSingFemale) {
|
||||
return script === "f" ? "zmaa" : "زما";
|
||||
}
|
||||
if (np.selection.person === T.Person.SecondSingMale || np.selection.person === T.Person.SecondSingFemale) {
|
||||
return script === "f" ? "staa" : "ستا";
|
||||
}
|
||||
if (np.selection.person === T.Person.FirstPlurMale || np.selection.person === T.Person.FirstPlurFemale) {
|
||||
return script === "f" ? "zmoonG" : "زمونږ";
|
||||
}
|
||||
if (np.selection.person === T.Person.SecondPlurMale || np.selection.person === T.Person.SecondPlurFemale) {
|
||||
return script === "f" ? "staaso" : "ستاسو";
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,20 @@
|
|||
import { baParticle } from "../../lib/grammar-units";
|
||||
import * as T from "../../types";
|
||||
import Pashto from "../Pashto";
|
||||
import Phonetics from "../Phonetics";
|
||||
|
||||
function KidDisplay({ opts, kid }: {
|
||||
function KidDisplay({ opts, kid, script }: {
|
||||
opts: T.TextOptions,
|
||||
kid: T.Kid,
|
||||
script: "p" | "f",
|
||||
}) {
|
||||
const ps = kid.type === "ba"
|
||||
? baParticle
|
||||
: kid.ps;
|
||||
return <div className="mx-1">
|
||||
{kid.type === "ba"
|
||||
? <Phonetics opts={opts}>{baParticle}</Phonetics>
|
||||
: <Phonetics opts={opts}>{kid.ps}</Phonetics>}
|
||||
{script === "p"
|
||||
? <Pashto opts={opts}>{ps}</Pashto>
|
||||
: <Phonetics opts={opts}>{ps}</Phonetics>}
|
||||
</div>
|
||||
}
|
||||
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
import * as T from "../../types";
|
||||
import Block from "../blocks/Block";
|
||||
import KidDisplay from "../blocks/KidDisplay";
|
||||
|
||||
function EPBlocksDisplay({ opts, rendered, justify }: {
|
||||
opts: T.TextOptions,
|
||||
rendered: T.EPRendered,
|
||||
justify?: "left" | "right" | "center",
|
||||
}) {
|
||||
const blocks = rendered.omitSubject
|
||||
? rendered.blocks.filter(b => b.type !== "subjectSelection")
|
||||
: rendered.blocks;
|
||||
return <div className={`d-flex flex-row justify-content-${justify ? justify : "center"}`} style={{}}>
|
||||
<div className="d-flex flex-row justify-content-left align-items-end mt-3 pb-2" style={{ overflowX: "auto" }}>
|
||||
<div key={Math.random()} className="mr-2">
|
||||
<Block opts={opts} block={blocks[0]} />
|
||||
</div>
|
||||
<KidsSection opts={opts} kids={rendered.kids} />
|
||||
{blocks.slice(1).map((block, i) => (
|
||||
<div key={Math.random()} className="mr-2">
|
||||
<Block opts={opts} block={block} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
function KidsSection({ opts, kids }: {
|
||||
opts: T.TextOptions,
|
||||
kids: T.Kid[],
|
||||
}) {
|
||||
return kids.length > 0 ? <div className="text-center mx-1 mr-3" style={{ paddingBottom: "1rem"}}>
|
||||
<div className="d-flex flex-row mb-3 justify-content-center">
|
||||
{kids.map(kid => (
|
||||
<KidDisplay key={Math.random()} opts={opts} kid={kid} />
|
||||
))}
|
||||
</div>
|
||||
<div><strong>kids</strong></div>
|
||||
</div> : null;
|
||||
}
|
||||
|
||||
export default EPBlocksDisplay;
|
|
@ -4,9 +4,10 @@ import { compileEP } from "../../lib/phrase-building/compile";
|
|||
import ButtonSelect from "../ButtonSelect";
|
||||
import { getPredicateSelectionFromBlocks, getSubjectSelection, getSubjectSelectionFromBlocks } from "../../lib/phrase-building/blocks-utils";
|
||||
import { useState } from "react";
|
||||
import EPTextDisplay from "./EPTextDisplay";
|
||||
import EPBlocksDisplay from "./EPBlocksDisplay";
|
||||
type Mode = "text" | "blocks";
|
||||
import CompiledPTextDisplay from "../CompiledPTextDisplay";
|
||||
import EPBlocksDisplay from "../RenderedBlocksDisplay";
|
||||
import ModeSelect, { Mode, ScriptSelect } from "../DisplayModeSelect";
|
||||
import { useStickyState } from "../../library";
|
||||
|
||||
function EPDisplay({ eps, opts, setOmitSubject, justify, onlyOne }: {
|
||||
eps: T.EPSelectionState,
|
||||
|
@ -16,6 +17,7 @@ function EPDisplay({ eps, opts, setOmitSubject, justify, onlyOne }: {
|
|||
onlyOne?: boolean,
|
||||
}) {
|
||||
const [mode, setMode] = useState<Mode>("text");
|
||||
const [script, setScript] = useStickyState<"p" | "f">("f", "blockScriptChoice");
|
||||
const EP = completeEPSelection(eps);
|
||||
const subject = getSubjectSelection(eps.blocks);
|
||||
|
||||
|
@ -36,7 +38,10 @@ function EPDisplay({ eps, opts, setOmitSubject, justify, onlyOne }: {
|
|||
const renderedPredicate = getPredicateSelectionFromBlocks(rendered.blocks).selection;
|
||||
return <div className="text-center pt-3">
|
||||
<div className="mb-2 d-flex flex-row justify-content-between align-items-center">
|
||||
<div className="d-flex flex-row">
|
||||
<ModeSelect value={mode} onChange={setMode} />
|
||||
{mode === "blocks" && <ScriptSelect value={script} onChange={setScript} />}
|
||||
</div>
|
||||
{setOmitSubject !== false ? <ButtonSelect
|
||||
small
|
||||
value={(eps.omitSubject ? "true" : "false") as "true" | "false"}
|
||||
|
@ -49,8 +54,8 @@ function EPDisplay({ eps, opts, setOmitSubject, justify, onlyOne }: {
|
|||
<div />
|
||||
</div>
|
||||
{mode === "text"
|
||||
? <EPTextDisplay opts={opts} compiled={result} justify={justify} onlyOne={onlyOne} />
|
||||
: <EPBlocksDisplay opts={opts} rendered={rendered} justify={justify} />}
|
||||
? <CompiledPTextDisplay opts={opts} compiled={result} justify={justify} onlyOne={onlyOne} />
|
||||
: <EPBlocksDisplay opts={opts} rendered={rendered} justify={justify} script={script} />}
|
||||
{result.e && <div className={`text-muted mt-2 text-${justify === "left" ? "left" : justify === "right" ? "right" : "center"}`}>
|
||||
{(onlyOne ? [result.e[0]] : result.e).map((e, i) => <div key={i}>{e}</div>)}
|
||||
</div>}
|
||||
|
@ -63,14 +68,4 @@ function EPDisplay({ eps, opts, setOmitSubject, justify, onlyOne }: {
|
|||
</div>
|
||||
}
|
||||
|
||||
function ModeSelect({ value, onChange }: { value: Mode, onChange: (m: Mode) => void }) {
|
||||
return <div style={{ fontSize: "larger" }}>
|
||||
{value === "text" ? <div className="clickable" onClick={() => onChange("blocks")}>
|
||||
<i className="fas fa-cubes" />
|
||||
</div> : <div className="clickable" onClick={() => onChange("text")}>
|
||||
<i className="fas fa-align-left" />
|
||||
</div>}
|
||||
</div>;
|
||||
}
|
||||
|
||||
export default EPDisplay;
|
|
@ -4,7 +4,7 @@ import {
|
|||
personNumber,
|
||||
} from "../../lib/misc-helpers";
|
||||
import { isUnisexNounEntry } from "../../lib/type-predicates";
|
||||
import { checkEPForMiniPronounsError } from "../../lib/phrase-building/compile";
|
||||
import { checkForMiniPronounsError } from "../../lib/phrase-building/compile";
|
||||
import { adjustSubjectSelection, getSubjectSelection, insertNewAP, removeAP, setAP, shiftBlock } from "../../lib/phrase-building/blocks-utils";
|
||||
|
||||
export type EpsReducerAction = {
|
||||
|
@ -189,7 +189,7 @@ export default function epsReducer(eps: T.EPSelectionState, action: EpsReducerAc
|
|||
}
|
||||
|
||||
function ensureMiniPronounsOk(old: T.EPSelectionState, eps: T.EPSelectionState, sendAlert?: (msg: string) => void): T.EPSelectionState {
|
||||
const error = checkEPForMiniPronounsError(eps);
|
||||
const error = checkForMiniPronounsError(eps);
|
||||
if (error) {
|
||||
if (sendAlert) sendAlert(error);
|
||||
return old;
|
||||
|
|
|
@ -1,56 +1,55 @@
|
|||
import { compileVP } from "../../lib/phrase-building/compile";
|
||||
import * as T from "../../types";
|
||||
import AbbreviationFormSelector from "./AbbreviationFormSelector";
|
||||
import Examples from "../Examples";
|
||||
import { getObjectSelection, getSubjectSelection } from "../../lib/phrase-building/blocks-utils";
|
||||
import { completeVPSelection } from "../../lib/phrase-building/vp-tools";
|
||||
import { renderVP } from "../../library";
|
||||
import ModeSelect, { Mode, ScriptSelect } from "../DisplayModeSelect";
|
||||
import { useState } from "react";
|
||||
import CompiledPTextDisplay from "../CompiledPTextDisplay";
|
||||
import RenderedBlocksDisplay from "../RenderedBlocksDisplay";
|
||||
|
||||
function VPDisplay({ VP, opts, setForm }: {
|
||||
VP: T.VPSelectionState | T.VPRendered,
|
||||
function VPDisplay({ VPS, opts, setForm, justify, onlyOne }: {
|
||||
VPS: T.VPSelectionState,
|
||||
opts: T.TextOptions,
|
||||
setForm: (form: T.FormVersion) => void,
|
||||
justify?: "left" | "right" | "center",
|
||||
onlyOne?: boolean,
|
||||
}) {
|
||||
if (!("type" in VP)) {
|
||||
const [mode, setMode] = useState<Mode>("text");
|
||||
const [script, setScript] = useState<"p" | "f">("f");
|
||||
const VP = completeVPSelection(VPS);
|
||||
if (!VP) {
|
||||
return <div className="lead text-muted text-center mt-4">
|
||||
{(() => {
|
||||
const subject = getSubjectSelection(VP.blocks).selection;
|
||||
const object = getObjectSelection(VP.blocks).selection;
|
||||
if (subject === undefined || object || undefined) {
|
||||
const subject = getSubjectSelection(VPS.blocks).selection;
|
||||
const object = getObjectSelection(VPS.blocks).selection;
|
||||
if (subject === undefined || object === undefined) {
|
||||
return `Choose NP${((subject === undefined) && (object === undefined)) ? "s " : ""} to make a phrase`;
|
||||
}
|
||||
return `Choose/remove AP to complete the phrase`;
|
||||
})()}
|
||||
</div>;
|
||||
}
|
||||
const result = compileVP(VP, { ...VP.form });
|
||||
const rendered = renderVP(VP);
|
||||
const result = compileVP(rendered, rendered.form);
|
||||
return <div className="text-center mt-1">
|
||||
<AbbreviationFormSelector
|
||||
adjustable={VP.whatsAdjustable}
|
||||
form={VP.form}
|
||||
adjustable={rendered.whatsAdjustable}
|
||||
form={rendered.form}
|
||||
onChange={setForm}
|
||||
/>
|
||||
{"long" in result.ps ?
|
||||
<div>
|
||||
{/* <div className="h6">Long Verb:</div> */}
|
||||
<VariationLayer vs={result.ps.long} opts={opts} />
|
||||
{/* <div className="h6">Short Verb:</div> */}
|
||||
<VariationLayer vs={result.ps.short} opts={opts} />
|
||||
{result.ps.mini && <>
|
||||
{/* <div className="h6">Mini Verb:</div> */}
|
||||
<VariationLayer vs={result.ps.mini} opts={opts} />
|
||||
</>}
|
||||
<div className="d-flex flex-row">
|
||||
<ModeSelect value={mode} onChange={setMode} />
|
||||
{mode === "blocks" && <ScriptSelect value={script} onChange={setScript} />}
|
||||
</div>
|
||||
: <VariationLayer vs={result.ps} opts={opts} />
|
||||
}
|
||||
{mode === "text"
|
||||
? <CompiledPTextDisplay opts={opts} compiled={result} justify={justify} onlyOne={onlyOne} />
|
||||
: <RenderedBlocksDisplay opts={opts} rendered={rendered} justify={justify} script={script} />}
|
||||
{result.e && <div className="text-muted mt-3">
|
||||
{result.e.map((e, i) => <div key={i}>{e}</div>)}
|
||||
</div>}
|
||||
</div>
|
||||
}
|
||||
|
||||
function VariationLayer({ vs, opts }: { vs: T.PsString[], opts: T.TextOptions }) {
|
||||
return <div className="mb-2">
|
||||
<Examples opts={opts} lineHeight={0}>{vs}</Examples>
|
||||
</div>;
|
||||
}
|
||||
|
||||
export default VPDisplay;
|
|
@ -15,7 +15,6 @@ import VPExplorerExplanationModal, { roleIcon } from "./VPExplorerExplanationMod
|
|||
// @ts-ignore
|
||||
import LZString from "lz-string";
|
||||
import { vpsReducer } from "./vps-reducer";
|
||||
import { getShrunkenServant } from "../../lib/phrase-building/compile";
|
||||
import APPicker from "../ap-picker/APPicker";
|
||||
import autoAnimate from "@formkit/auto-animate";
|
||||
import { getObjectSelection, getSubjectSelection, isNoObject } from "../../lib/phrase-building/blocks-utils";
|
||||
|
@ -44,7 +43,7 @@ function VPExplorer(props: {
|
|||
props.loaded
|
||||
? props.loaded
|
||||
: savedVps => makeVPSelectionState(props.verb, savedVps),
|
||||
"vpsState14",
|
||||
"vpsState15",
|
||||
flashMessage,
|
||||
);
|
||||
const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">(
|
||||
|
@ -143,7 +142,7 @@ function VPExplorer(props: {
|
|||
const VPS = completeVPSelection(vps);
|
||||
const phraseIsComplete = !!VPS;
|
||||
const rendered = VPS ? renderVP(VPS) : undefined;
|
||||
const servantIsShrunk = !!(rendered ? getShrunkenServant(rendered) : undefined);
|
||||
const servantIsShrunk = includesShrunkenServant(rendered?.kids);
|
||||
function toggleServantShrink() {
|
||||
adjustVps({
|
||||
type: "toggle servant shrink",
|
||||
|
@ -257,7 +256,12 @@ function VPExplorer(props: {
|
|||
: (vps.verb && block?.type === "objectSelection" && block.selection !== "none")
|
||||
? <div className="my-2" style={{ background: (servantIsShrunk && roles.servant === "object") ? shrunkenBackground : "inherit" }}>
|
||||
{(typeof block.selection === "number")
|
||||
? <div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div>
|
||||
? <div>
|
||||
{roles.king === "object"
|
||||
? <div className="h5 text-center clickable" onClick={() => setShowingExplanation({ role: "king", item: "object" })}>Object {roleIcon.king}</div>
|
||||
: <div className="h5 text-center">Object</div>}
|
||||
<div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div>
|
||||
</div>
|
||||
: <NPPicker
|
||||
phraseIsComplete={phraseIsComplete}
|
||||
heading={roles.king === "object"
|
||||
|
@ -295,7 +299,7 @@ function VPExplorer(props: {
|
|||
</div>
|
||||
</div>}
|
||||
{mode === "phrases" && <VPDisplay
|
||||
VP={rendered ? rendered : vps}
|
||||
VPS={vps}
|
||||
opts={props.opts}
|
||||
setForm={handleSetForm}
|
||||
/>}
|
||||
|
@ -351,3 +355,10 @@ function getVPSFromUrl(): T.VPSelectionState | undefined {
|
|||
return JSON.parse(decoded) as T.VPSelectionState;
|
||||
}
|
||||
|
||||
function includesShrunkenServant(kids?: T.Kid[]): boolean {
|
||||
if (!kids) return false;
|
||||
return kids.some(k => (
|
||||
k.type === "mini-pronoun" && k.source === "servant"
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -15,10 +15,10 @@ import playAudio from "../../lib/play-audio";
|
|||
import TensePicker from "./TensePicker";
|
||||
import Keyframes from "../Keyframes";
|
||||
import energyDrink from "./energy-drink.jpg";
|
||||
import { flattenLengths } from "../../lib/phrase-building/segment";
|
||||
import { flattenLengths } from "../../lib/phrase-building/compile";
|
||||
import { concatPsString } from "../../lib/p-text-helpers";
|
||||
import { isImperativeTense } from "../../lib/type-predicates";
|
||||
import { adjustObjectSelection, adjustSubjectSelection, getObjectSelection, getRenderedObjectSelection, getRenderedSubjectSelection, getSubjectSelection } from "../../lib/phrase-building/blocks-utils";
|
||||
import { adjustObjectSelection, adjustSubjectSelection, getObjectSelection, getObjectSelectionFromBlocks, getSubjectSelectionFromBlocks, getSubjectSelection, getVerbAndHeadFromBlocks } from "../../lib/phrase-building/blocks-utils";
|
||||
|
||||
const correctEmoji = ["✅", '🤓', "✅", '😊', "🌹", "✅", "✅", '🥳', "👏", "✅", "💯", "😎", "✅", "👍"];
|
||||
|
||||
|
@ -113,8 +113,8 @@ function VPExplorerQuiz(props: {
|
|||
}
|
||||
}
|
||||
const rendered = renderVP(quizState.vps);
|
||||
const subject: T.Rendered<T.NPSelection> = getRenderedSubjectSelection(rendered.blocks).selection;
|
||||
const object = getRenderedObjectSelection(rendered.blocks).selection;
|
||||
const subject: T.Rendered<T.NPSelection> = getSubjectSelectionFromBlocks(rendered.blocks).selection;
|
||||
const object = getObjectSelectionFromBlocks(rendered.blocks).selection;
|
||||
const { e } = compileVP(rendered, { removeKing: false, shrinkServant: false });
|
||||
return <div className="mt-4">
|
||||
<ProgressBar quizState={quizState} />
|
||||
|
@ -346,18 +346,17 @@ function tickQuizState(startingWith: T.VPSelectionComplete | QuizState): QuizSta
|
|||
}
|
||||
|
||||
function getBlanksAnswer(vps: T.VPSelectionComplete): { ps: T.PsString[], withBa: boolean } {
|
||||
const { verb } = renderVP(vps);
|
||||
const { head, rest } = verb.ps;
|
||||
const ps = flattenLengths(rest).map(x => {
|
||||
const { verb, perfectiveHead } = getVerbAndHeadFromBlocks(renderVP(vps).blocks);
|
||||
const ps = flattenLengths(verb.block.ps).map(x => {
|
||||
const y = removeBa(x);
|
||||
if (head) {
|
||||
return concatPsString(head, y);
|
||||
if (perfectiveHead) {
|
||||
return concatPsString(perfectiveHead.ps, y);
|
||||
}
|
||||
return y;
|
||||
});
|
||||
return {
|
||||
ps,
|
||||
withBa: verb.hasBa,
|
||||
withBa: verb.block.hasBa,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,518 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2021 lingdocs.com
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
|
||||
import {
|
||||
ensureBaAt,
|
||||
isAllOne,
|
||||
isVerbBlock,
|
||||
removeHead,
|
||||
uniquePsStringArray,
|
||||
splitOffLeapfrogWord,
|
||||
removeObjComp,
|
||||
psRemove,
|
||||
psStringContains,
|
||||
} from "../lib/p-text-helpers";
|
||||
import {
|
||||
getPersonFromVerbForm,
|
||||
pickPersInf,
|
||||
} from "../lib/misc-helpers";
|
||||
import {
|
||||
baParticle,
|
||||
pronouns,
|
||||
} from "../lib/grammar-units";
|
||||
import {
|
||||
removeAccents,
|
||||
} from "../lib/accent-helpers";
|
||||
import { concatPsString } from "../lib/p-text-helpers";
|
||||
import * as T from "../types";
|
||||
const pashtoCharacterRange = "\u0621-\u065f\u0670-\u06d3\u06d5"
|
||||
|
||||
function getSplitHead(split: T.SplitInfo | undefined, matrixKey: T.PersonInflectionsField) {
|
||||
if (!split) {
|
||||
return undefined;
|
||||
}
|
||||
const fromMatrix = pickPersInf(split, matrixKey)
|
||||
// doesn't matter what length it is, the head will always be the same
|
||||
const pair = "long" in fromMatrix ? fromMatrix.long : fromMatrix;
|
||||
return pair[0];
|
||||
}
|
||||
|
||||
function formHasVariations(form: T.VerbForm | T.ImperativeForm | T.ParticipleForm | T.SentenceForm): boolean {
|
||||
if ("mascSing" in form) {
|
||||
return formHasVariations(form.mascSing);
|
||||
}
|
||||
if ("long" in form) {
|
||||
return formHasVariations(form.long);
|
||||
}
|
||||
if (!isVerbBlock(form)) {
|
||||
return false;
|
||||
}
|
||||
return !isAllOne(form);
|
||||
}
|
||||
|
||||
type Pronouns = undefined | {
|
||||
subject: T.PsString | [T.PsString, T.PsString],
|
||||
object?: T.PsString | [T.PsString, T.PsString],
|
||||
mini: T.PsString,
|
||||
}
|
||||
|
||||
const nuParticle = { p: "نه", f: "nú" };
|
||||
|
||||
export default function addPronouns({ s, subject, object, info, displayForm, intransitive, ergative, matrixKey, englishConjugation, negative, sentenceLevel = "hard" }: {
|
||||
s: T.SentenceForm,
|
||||
subject: T.Person,
|
||||
object: T.Person,
|
||||
info: T.NonComboVerbInfo,
|
||||
displayForm: T.DisplayFormForSentence,
|
||||
intransitive: boolean,
|
||||
ergative: boolean,
|
||||
matrixKey: T.PersonInflectionsField,
|
||||
negative: boolean,
|
||||
englishConjugation?: T.EnglishVerbConjugation,
|
||||
sentenceLevel?: "easy" | "medium" | "hard",
|
||||
}): T.SentenceForm {
|
||||
if ("long" in s) {
|
||||
return {
|
||||
long: addPronouns({ s: s.long, subject, object, info, displayForm, intransitive, ergative, matrixKey, englishConjugation, negative, sentenceLevel }) as T.ArrayOneOrMore<T.PsString>,
|
||||
short: addPronouns({ s: s.short, subject, object, info, displayForm, intransitive, ergative, matrixKey, englishConjugation, negative, sentenceLevel }) as T.ArrayOneOrMore<T.PsString>,
|
||||
...s.mini ? {
|
||||
mini: addPronouns({ s: s.mini, subject, object, info, displayForm, intransitive, ergative, matrixKey, englishConjugation, negative, sentenceLevel }) as T.ArrayOneOrMore<T.PsString>,
|
||||
} : {},
|
||||
}
|
||||
}
|
||||
function makeEnglish(englishBuilder: T.EnglishBuilder, englishConjugation: T.EnglishVerbConjugation): string[] {
|
||||
const noObject = (intransitive || info.transitivity === "grammatically transitive" || info.type === "dynamic compound");
|
||||
const addRest = (s: string) => (
|
||||
`${s}${noObject ? "" : ` ${engObj(object)}`}${englishConjugation.ep ? ` ${englishConjugation.ep}` : ""}`
|
||||
);
|
||||
return englishBuilder(subject, englishConjugation.ec, negative)
|
||||
.map(addRest);
|
||||
}
|
||||
const firstOrSecondObjectPresent = [0,1,2,3,6,7,8,9].includes(object) && !displayForm.past;
|
||||
const nearPronounPossible = (p: T.Person) => [4, 5, 10, 11].includes(p);
|
||||
const noPronouns =
|
||||
info.transitivity === "grammatically transitive" && displayForm.passive;
|
||||
const noObjectPronoun =
|
||||
intransitive || info.transitivity === "grammatically transitive" ||
|
||||
info.type === "dynamic compound" || info.type === "generative stative compound";
|
||||
const transDynCompPast =
|
||||
info.transitivity === "transitive" && info.type === "dynamic compound" && displayForm.past;
|
||||
const subjectPronoun = (getPersonFromVerbForm(
|
||||
pronouns.far[ergative ? "inflected" : "plain"],
|
||||
subject,
|
||||
) as T.ArrayOneOrMore<T.PsString>)[0];
|
||||
const nearSubjectPronoun = (getPersonFromVerbForm(
|
||||
pronouns.near[ergative ? "inflected" : "plain"],
|
||||
subject,
|
||||
) as T.ArrayOneOrMore<T.PsString>)[0];
|
||||
const objectPronoun = (getPersonFromVerbForm(
|
||||
pronouns.far[firstOrSecondObjectPresent ? "inflected" : "plain"],
|
||||
object,
|
||||
) as T.ArrayOneOrMore<T.PsString>)[0];
|
||||
const nearObjectPronoun = (getPersonFromVerbForm(
|
||||
pronouns.near[firstOrSecondObjectPresent ? "inflected" : "plain"],
|
||||
object,
|
||||
) as T.ArrayOneOrMore<T.PsString>)[0];
|
||||
const miniPronoun = (getPersonFromVerbForm(
|
||||
pronouns.mini,
|
||||
ergative ? subject : object,
|
||||
) as T.ArrayOneOrMore<T.PsString>)[0];
|
||||
|
||||
const prns: Pronouns = noPronouns
|
||||
? undefined
|
||||
: noObjectPronoun
|
||||
? {
|
||||
subject: ((sentenceLevel === "hard") && nearPronounPossible(subject)) ? [subjectPronoun, nearSubjectPronoun] : subjectPronoun,
|
||||
mini: miniPronoun,
|
||||
} : {
|
||||
subject: ((sentenceLevel === "hard") && nearPronounPossible(subject)) ? [subjectPronoun, nearSubjectPronoun] : subjectPronoun,
|
||||
object: ((sentenceLevel === "hard") && nearPronounPossible(object)) ? [objectPronoun, nearObjectPronoun] : objectPronoun,
|
||||
mini: miniPronoun,
|
||||
};
|
||||
const english = (displayForm.englishBuilder && englishConjugation)
|
||||
? makeEnglish(displayForm.englishBuilder, englishConjugation)
|
||||
: undefined;
|
||||
|
||||
function attachPronounsToVariation(ps: T.PsString, prns: Pronouns): T.ArrayOneOrMore<T.PsString> {
|
||||
if (!prns) {
|
||||
return [ps];
|
||||
}
|
||||
if (Array.isArray(prns.subject)) {
|
||||
return [
|
||||
...attachPronounsToVariation(ps, { ...prns, subject: prns.subject[0] }),
|
||||
...attachPronounsToVariation(ps, { ...prns, subject: prns.subject[1] }),
|
||||
] as T.ArrayOneOrMore<T.PsString>;
|
||||
}
|
||||
if (Array.isArray(prns.object)) {
|
||||
return [
|
||||
...attachPronounsToVariation(ps, { ...prns, object: prns.object[0] }),
|
||||
...attachPronounsToVariation(ps, { ...prns, object: prns.object[1] }),
|
||||
] as T.ArrayOneOrMore<T.PsString>;
|
||||
}
|
||||
const splitHead = (displayForm.aspect && displayForm.aspect === "perfective")
|
||||
? getSplitHead(info[displayForm.past ? "root" : "stem"].perfectiveSplit, matrixKey)
|
||||
: undefined;
|
||||
const basicForms = (!prns.object)
|
||||
// basic form with only one pronoun
|
||||
? makeBasicPronounForm(ps, splitHead, displayForm, info, negative, prns.subject)
|
||||
: [
|
||||
// basic form two full pronouns
|
||||
...makeBasicPronounForm(ps, splitHead, displayForm, info, negative, prns.subject, prns.object),
|
||||
// basic form one full, one mini pronoun
|
||||
...sentenceLevel !== "easy" ? makeBasicPronounForm(
|
||||
ps,
|
||||
splitHead,
|
||||
displayForm,
|
||||
info,
|
||||
negative,
|
||||
ergative ? prns.object : prns.subject,
|
||||
prns.mini,
|
||||
) : [],
|
||||
] as T.ArrayOneOrMore<T.PsString>;
|
||||
|
||||
const ergativeGrammTrans = (info.transitivity === "grammatically transitive" && ergative);
|
||||
const canWorkWithOnlyMini = (prns.object && !displayForm.secondPronounNeeded && formHasVariations(displayForm.form))
|
||||
|| transDynCompPast || ergativeGrammTrans;
|
||||
return [
|
||||
...basicForms,
|
||||
...(sentenceLevel !== "easy" && canWorkWithOnlyMini)
|
||||
? makeOnlyMiniForm(ps, splitHead, displayForm, info, negative, prns.mini)
|
||||
: [],
|
||||
].map((ps) => english ? { ...ps, e: english } : ps) as T.ArrayOneOrMore<T.PsString>;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
return s.reduce((variations, current) => (
|
||||
[...variations, ...uniquePsStringArray(
|
||||
attachPronounsToVariation(current, prns)
|
||||
)]
|
||||
), []) as T.ArrayOneOrMore<T.PsString>;
|
||||
}
|
||||
|
||||
function nuMustGoAfterSplitHead(head: T.PsString) {
|
||||
return (
|
||||
["و", "وا"].includes(head.p)
|
||||
||
|
||||
head.p.slice(-1) === " " // compound splits
|
||||
||
|
||||
head.p.match(`[${pashtoCharacterRange}]* و`)
|
||||
);
|
||||
}
|
||||
|
||||
function spaceAfterSplitHead(head: T.PsString) {
|
||||
if (nuMustGoAfterSplitHead(head) && head.p.slice(-1) !== " ") {
|
||||
return { p: "", f: "-" }
|
||||
}
|
||||
return { p: " ", f: " " };
|
||||
}
|
||||
|
||||
function makeBasicPronounForm(
|
||||
ps: T.PsString,
|
||||
splitHead: T.PsString | undefined,
|
||||
displayForm: T.DisplayFormForSentence,
|
||||
info: T.NonComboVerbInfo,
|
||||
negative: boolean,
|
||||
firstPronoun: T.PsString,
|
||||
secondPronoun?: T.PsString,
|
||||
): T.PsString[] {
|
||||
if (!negative) {
|
||||
return [
|
||||
ensureBaAt(
|
||||
concatPsString(
|
||||
firstPronoun,
|
||||
" ",
|
||||
secondPronoun ? concatPsString(secondPronoun, " ") : "",
|
||||
ps,
|
||||
),
|
||||
1)
|
||||
];
|
||||
}
|
||||
const objComplement = getObjComplement(info);
|
||||
function negativeWOutSplit() {
|
||||
if (!displayForm.reorderWithNegative) {
|
||||
return [
|
||||
ensureBaAt(
|
||||
concatPsString(
|
||||
firstPronoun,
|
||||
" ",
|
||||
secondPronoun
|
||||
? concatPsString(secondPronoun, " ")
|
||||
: objComplement
|
||||
? concatPsString(objComplement, " ")
|
||||
: "",
|
||||
nuParticle,
|
||||
" ",
|
||||
removeAccents(objComplement ? removeObjComp(objComplement, ps) : ps)
|
||||
),
|
||||
1),
|
||||
];
|
||||
}
|
||||
const [beginning, end] = splitOffLeapfrogWord(ps);
|
||||
return [
|
||||
ensureBaAt(
|
||||
objComplement ?
|
||||
concatPsString(
|
||||
firstPronoun,
|
||||
" ",
|
||||
objComplement,
|
||||
" ",
|
||||
nuParticle,
|
||||
" ",
|
||||
end,
|
||||
" ",
|
||||
removeAccents(removeObjComp(objComplement, beginning)),
|
||||
)
|
||||
: concatPsString(
|
||||
firstPronoun,
|
||||
" ",
|
||||
secondPronoun ? concatPsString(secondPronoun, " ") : "",
|
||||
nuParticle,
|
||||
" ",
|
||||
end,
|
||||
" ",
|
||||
removeAccents(beginning),
|
||||
),
|
||||
1),
|
||||
ensureBaAt(
|
||||
concatPsString(
|
||||
firstPronoun,
|
||||
" ",
|
||||
secondPronoun ? concatPsString(secondPronoun, " ") : "",
|
||||
removeAccents(beginning),
|
||||
" ",
|
||||
nuParticle,
|
||||
" ",
|
||||
end,
|
||||
),
|
||||
1),
|
||||
];
|
||||
}
|
||||
function insertNegInSplit(splitHead: T.PsString) {
|
||||
if (!displayForm.reorderWithNegative) {
|
||||
return [
|
||||
ensureBaAt(
|
||||
concatPsString(
|
||||
firstPronoun,
|
||||
" ",
|
||||
secondPronoun ? concatPsString(secondPronoun, " ") : "",
|
||||
removeAccents(splitHead),
|
||||
spaceAfterSplitHead(splitHead),
|
||||
nuParticle,
|
||||
" ",
|
||||
removeHead(splitHead, ps),
|
||||
),
|
||||
1),
|
||||
];
|
||||
}
|
||||
const [beginning, end] = splitOffLeapfrogWord(ps);
|
||||
return [
|
||||
ensureBaAt(
|
||||
concatPsString(
|
||||
firstPronoun,
|
||||
" ",
|
||||
secondPronoun ? concatPsString(secondPronoun, " ") : "",
|
||||
removeAccents(splitHead),
|
||||
spaceAfterSplitHead(splitHead),
|
||||
nuParticle,
|
||||
" ",
|
||||
end,
|
||||
" ",
|
||||
removeHead(splitHead, beginning),
|
||||
),
|
||||
1),
|
||||
ensureBaAt(
|
||||
concatPsString(
|
||||
firstPronoun,
|
||||
" ",
|
||||
secondPronoun ? concatPsString(secondPronoun, " ") : "",
|
||||
removeAccents(splitHead),
|
||||
spaceAfterSplitHead(splitHead),
|
||||
nuParticle,
|
||||
" ",
|
||||
removeHead(splitHead, beginning),
|
||||
" ",
|
||||
end,
|
||||
),
|
||||
1),
|
||||
];
|
||||
}
|
||||
if (splitHead) {
|
||||
return nuMustGoAfterSplitHead(splitHead) ? insertNegInSplit(splitHead) : [
|
||||
...insertNegInSplit(splitHead),
|
||||
...negativeWOutSplit(),
|
||||
]
|
||||
}
|
||||
return negativeWOutSplit();
|
||||
}
|
||||
|
||||
function makeOnlyMiniForm(
|
||||
ps: T.PsString,
|
||||
splitHead: T.PsString | undefined,
|
||||
displayForm: T.DisplayFormForSentence,
|
||||
info: T.NonComboVerbInfo,
|
||||
negative: boolean,
|
||||
mini: T.PsString,
|
||||
): T.PsString[] {
|
||||
const objComplement = getObjComplement(info);
|
||||
function reorderedNegativeAfterSplitHead(splitHead: T.PsString) {
|
||||
const [beginning, end] = splitOffLeapfrogWord(ps);
|
||||
return ensureBaAt(
|
||||
objComplement ?
|
||||
concatPsString(
|
||||
objComplement,
|
||||
" ",
|
||||
mini,
|
||||
" ",
|
||||
removeAccents(removeObjComp(objComplement, splitHead)),
|
||||
spaceAfterSplitHead(splitHead),
|
||||
nuParticle,
|
||||
" ",
|
||||
end,
|
||||
" ",
|
||||
removeHead(splitHead, beginning),
|
||||
)
|
||||
: concatPsString(
|
||||
removeAccents(splitHead),
|
||||
spaceAfterSplitHead(splitHead),
|
||||
mini,
|
||||
" ",
|
||||
nuParticle,
|
||||
" ",
|
||||
end,
|
||||
" ",
|
||||
removeHead(splitHead, beginning),
|
||||
),
|
||||
1)
|
||||
}
|
||||
if (splitHead) {
|
||||
// only mini pronoun with split
|
||||
if (!displayForm.reorderWithNegative || !negative) {
|
||||
const safeSplitHead = removeObjComp(objComplement, splitHead);
|
||||
return [ensureBaAt(
|
||||
concatPsString(
|
||||
objComplement ? concatPsString(objComplement, " ", mini, " ") : "",
|
||||
negative ? removeAccents(safeSplitHead) : safeSplitHead,
|
||||
spaceAfterSplitHead(safeSplitHead),
|
||||
!objComplement ? concatPsString(mini, " ") : "",
|
||||
negative ? concatPsString(nuParticle, " ") : "",
|
||||
removeHead(splitHead, ps)
|
||||
),
|
||||
1)];
|
||||
}
|
||||
// if (!nuMustGoAfterSplitHead(splitHead)) {
|
||||
// TODO: IS THIS A SEPERATELY NECESSARY THING FOR VERBS LIKE
|
||||
// PREXODUL -- LIKE COULD YOU ALSO DO A VERSION WHERE THE SPLIT ISN'T USED
|
||||
// return [reorderedNegativeAfterSplitHead(splitHead)];
|
||||
// }
|
||||
return [reorderedNegativeAfterSplitHead(splitHead)];
|
||||
}
|
||||
// only mini without split
|
||||
const [beginning, end] = splitOffLeapfrogWord(ps);
|
||||
if (!displayForm.reorderWithNegative || !negative) {
|
||||
if (objComplement) {
|
||||
return [
|
||||
concatPsString(
|
||||
objComplement,
|
||||
psStringContains(ps, concatPsString(baParticle, " ")) ? concatPsString(" ", baParticle, " ") : " ",
|
||||
concatPsString(mini, " "),
|
||||
negative ? concatPsString(" ", nuParticle, " ") : "",
|
||||
removeObjComp(objComplement, psRemove(ps, concatPsString(baParticle, " "))),
|
||||
)
|
||||
];
|
||||
}
|
||||
return [
|
||||
concatPsString(
|
||||
psRemove(beginning, concatPsString(baParticle, " ")),
|
||||
" ",
|
||||
psStringContains(beginning, concatPsString(baParticle, " ")) ? concatPsString(baParticle, " ") : "",
|
||||
negative ? concatPsString(" ", nuParticle, " ") : " ",
|
||||
(beginning.p || negative) ? concatPsString(mini, " ") : "",
|
||||
end,
|
||||
(beginning.p || negative) ? "" : concatPsString(" ", mini),
|
||||
),
|
||||
];
|
||||
}
|
||||
if (objComplement) {
|
||||
return [
|
||||
ensureBaAt(
|
||||
concatPsString(
|
||||
objComplement,
|
||||
" ",
|
||||
mini,
|
||||
" ",
|
||||
nuParticle,
|
||||
" ",
|
||||
end,
|
||||
" ",
|
||||
removeObjComp(objComplement, beginning),
|
||||
),
|
||||
1),
|
||||
ensureBaAt(
|
||||
concatPsString(
|
||||
objComplement,
|
||||
" ",
|
||||
mini,
|
||||
" ",
|
||||
removeObjComp(objComplement, beginning),
|
||||
" ",
|
||||
nuParticle,
|
||||
" ",
|
||||
end,
|
||||
),
|
||||
1),
|
||||
]
|
||||
}
|
||||
return [
|
||||
ensureBaAt(
|
||||
concatPsString(
|
||||
beginning,
|
||||
" ",
|
||||
mini,
|
||||
" ",
|
||||
nuParticle,
|
||||
" ",
|
||||
end,
|
||||
),
|
||||
1),
|
||||
ensureBaAt(
|
||||
concatPsString(
|
||||
nuParticle,
|
||||
" ",
|
||||
end,
|
||||
" ",
|
||||
mini,
|
||||
" ",
|
||||
beginning,
|
||||
),
|
||||
1),
|
||||
];
|
||||
}
|
||||
|
||||
function getObjComplement(info: T.NonComboVerbInfo): T.PsString | undefined {
|
||||
return info.type === "dynamic compound" ?
|
||||
(info.objComplement.plural ? info.objComplement.plural : info.objComplement.entry) :
|
||||
undefined;
|
||||
}
|
||||
|
||||
function engObj(s: T.Person): string {
|
||||
return (s === T.Person.FirstSingMale || s === T.Person.FirstSingFemale)
|
||||
? "me"
|
||||
: (s === T.Person.FirstPlurMale || s === T.Person.FirstPlurFemale)
|
||||
? "us"
|
||||
: (s === T.Person.SecondSingMale || s === T.Person.SecondSingFemale)
|
||||
? "you"
|
||||
: (s === T.Person.SecondPlurMale || s === T.Person.SecondPlurFemale)
|
||||
? "you (pl.)"
|
||||
: (s === T.Person.ThirdSingMale)
|
||||
? "him/it"
|
||||
: (s === T.Person.ThirdSingFemale)
|
||||
? "her/it"
|
||||
: (s === T.Person.ThirdPlurMale)
|
||||
? "them"
|
||||
: "them (f.)";
|
||||
}
|
|
@ -1,740 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2021 lingdocs.com
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
|
||||
// FOR ENGLISH FORMS
|
||||
// GIVEN VERB INFO LIKE THIS
|
||||
// ["hit", "hits", "hitting", "hit", "hit"]
|
||||
// ["eat", "eats", "eating", "ate", "eaten"]
|
||||
// ["see", "sees", "seeing", "saw", "seen"]
|
||||
|
||||
// Present Perfect
|
||||
|
||||
// Past Perfect
|
||||
// - subj "had" (N && "not") v.4 obj
|
||||
|
||||
// Future Perfect
|
||||
// - subj "will have" (N && "not") v.4 obj
|
||||
|
||||
|
||||
|
||||
import {
|
||||
getPersonInflectionsKey,
|
||||
pickPersInf,
|
||||
getPersonFromVerbForm,
|
||||
getVerbBlockPosFromPerson,
|
||||
} from "./misc-helpers";
|
||||
import addPronouns from "./add-pronouns";
|
||||
import * as T from "../types";
|
||||
import { englishEquative } from "./grammar-units";
|
||||
|
||||
type FilterFunc = (form: any) => boolean;
|
||||
type MapFunc = (opts: {
|
||||
subject: T.Person,
|
||||
object: T.Person,
|
||||
displayForm: T.DisplayFormForSentence,
|
||||
info: T.NonComboVerbInfo,
|
||||
negative: boolean,
|
||||
englishConjugation?: T.EnglishVerbConjugation,
|
||||
sentenceLevel?: "easy" | "medium" | "hard",
|
||||
}) => T.DisplayFormItem;
|
||||
|
||||
/**
|
||||
* Used to apply a filter function on both the levels of forms and subgroups
|
||||
*
|
||||
* @param input
|
||||
* @param func
|
||||
*/
|
||||
const formFilter = (
|
||||
input: T.DisplayFormItem[],
|
||||
func: FilterFunc | FilterFunc[]
|
||||
): T.DisplayFormItem[] => {
|
||||
// TODO: Better filtering that lets us filter things only in sub categories
|
||||
|
||||
// recursive madness to apply an array of filters 🤪
|
||||
// i'm doing this because I couldn't get a compose function to work 🤷♂️
|
||||
if (Array.isArray(func)) {
|
||||
if (func.length === 0) return input;
|
||||
return formFilter(
|
||||
formFilter(input, func[0]),
|
||||
func.slice(1),
|
||||
);
|
||||
}
|
||||
return (
|
||||
input.filter(func)
|
||||
.map((f) => (
|
||||
"content" in f
|
||||
? { ...f, content: f.content.filter(func) }
|
||||
: f
|
||||
))
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to apply a filter function on both the levels of forms and subgroups
|
||||
*
|
||||
* @param input
|
||||
* @param func
|
||||
*/
|
||||
const formMap = (
|
||||
input: T.DisplayFormItem[],
|
||||
func: MapFunc,
|
||||
info: T.NonComboVerbInfo,
|
||||
subject: T.Person,
|
||||
object: T.Person,
|
||||
negative: boolean,
|
||||
englishConjugation?: T.EnglishVerbConjugation,
|
||||
sentenceLevel?: "easy" | "medium" | "hard",
|
||||
): T.DisplayFormItem[] => {
|
||||
return input.map((f) => (
|
||||
"content" in f
|
||||
? { ...f, content: formMap(f.content, func, info, subject, object, negative, englishConjugation, sentenceLevel) }
|
||||
: func({ displayForm: f as T.DisplayFormForSentence, info, subject, object, negative, englishConjugation, sentenceLevel })
|
||||
));
|
||||
};
|
||||
|
||||
const makeSentence = ({ subject, object, info, displayForm, englishConjugation, negative, sentenceLevel }: {
|
||||
subject: T.Person,
|
||||
object: T.Person,
|
||||
info: T.NonComboVerbInfo,
|
||||
displayForm: T.DisplayFormForSentence,
|
||||
negative: boolean,
|
||||
sentenceLevel?: "easy" | "medium" | "hard",
|
||||
englishConjugation?: T.EnglishVerbConjugation,
|
||||
}): T.DisplayForm => {
|
||||
const intransitive = info.transitivity === "intransitive" || !!displayForm.passive;
|
||||
const ergative = !intransitive && !!displayForm.past;
|
||||
function chooseConjugation(g: T.SingleOrLengthOpts<T.VerbBlock>): T.SentenceForm {
|
||||
const person = ergative
|
||||
? object
|
||||
: subject;
|
||||
return getPersonFromVerbForm(g, person);
|
||||
}
|
||||
const f = displayForm.form;
|
||||
// IMPORTANT TODO!!! -- IS THIS ALWAYS THE OBJECT HERE?
|
||||
const matrixKey = getPersonInflectionsKey(object);
|
||||
const matrixChosen = pickPersInf(f, matrixKey);
|
||||
const conjugationChosen = chooseConjugation(matrixChosen);
|
||||
const form = addPronouns({
|
||||
s: conjugationChosen,
|
||||
subject,
|
||||
object,
|
||||
info,
|
||||
displayForm,
|
||||
intransitive,
|
||||
ergative,
|
||||
matrixKey,
|
||||
negative,
|
||||
englishConjugation,
|
||||
sentenceLevel,
|
||||
});
|
||||
return {
|
||||
...displayForm,
|
||||
form,
|
||||
};
|
||||
}
|
||||
|
||||
function isToBe(v: T.EnglishVerbConjugationEc): boolean {
|
||||
return (v[2] === "being");
|
||||
}
|
||||
|
||||
const futureEngBuilder: T.EnglishBuilder = (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} will${n ? " not" : ""} ${isToBe(v) ? "be" : v[0]}`,
|
||||
]);
|
||||
|
||||
const formsOfConjugation = (conj: T.VerbConjugation): T.DisplayFormItem[] => [
|
||||
{
|
||||
label: "Present",
|
||||
aspect: "imperfective",
|
||||
form: conj.imperfective.nonImperative,
|
||||
formula: "Imperfective Stem + Present Ending",
|
||||
sentence: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} ${isToBe(v)
|
||||
? `${engEquative("present", s)}${n ? " not" : ""}`
|
||||
: `${n ? engPresC(s, ["don't", "doesn't"]) : ""} ${n ? v[0] : engPresC(s, v)}`}`,
|
||||
`${engSubj(s)} ${engEquative("present", s)}${n ? " not" : ""} ${v[2]}`,
|
||||
]),
|
||||
explanation: "Something that is happening, happens generally, or is definitely about to happen. ('I am ____ing', 'I _____')",
|
||||
},
|
||||
{
|
||||
label: "Subjunctive",
|
||||
aspect: "perfective",
|
||||
form: conj.perfective.nonImperative,
|
||||
formula: "Perfective Stem + Present Ending",
|
||||
sentence: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`that ${engSubj(s, true)}${n ? " won't" : " will"} ${isToBe(v) ? "be" : v[0]}`,
|
||||
`should ${engSubj(s, true)}${n ? " not" : ""} ${isToBe(v) ? "be" : v[0]}`,
|
||||
]),
|
||||
explanation: "Used for hypothetical statements about the desire, necessity, purpose, or possibility of something happening. Or for saying something should or shouldn't happen. ('Should I ____?', 'so that'll I'll _____')"
|
||||
},
|
||||
{
|
||||
label: "Imperfective Future",
|
||||
aspect: "imperfective",
|
||||
form: conj.imperfective.future,
|
||||
advanced: true,
|
||||
formula: "به - ba + Present",
|
||||
sentence: true,
|
||||
englishBuilder: futureEngBuilder,
|
||||
explanation: "Saying something will happen, repeatedly or as an ongoing action",
|
||||
},
|
||||
{
|
||||
label: "Perfective Future",
|
||||
aspect: "perfective",
|
||||
form: conj.perfective.future,
|
||||
advanced: true,
|
||||
formula: "به - ba + Subjunctive",
|
||||
sentence: true,
|
||||
englishBuilder: futureEngBuilder,
|
||||
explanation: "Saying something will happen as a one-time event - May also used when there is some doubt",
|
||||
},
|
||||
...conj.imperfective.imperative ?
|
||||
[{
|
||||
label: "Imperfective Imperative",
|
||||
aspect: "imperfective",
|
||||
form: conj.imperfective.imperative,
|
||||
formula: "Imperfective Stem + Imperative Ending",
|
||||
explanation: "Commanding someone/people to do something repeatedly, or in general",
|
||||
} as T.DisplayForm] : [],
|
||||
...conj.perfective.imperative ?
|
||||
[{
|
||||
label: "Perfective Imperative",
|
||||
aspect: "perfective",
|
||||
form: conj.perfective.imperative,
|
||||
formula: "Perfective Stem + Imperative Ending",
|
||||
explanation: "Commanding someone/people to do something one time",
|
||||
} as T.DisplayForm] : [],
|
||||
{
|
||||
label: "Continuous Past",
|
||||
aspect: "imperfective",
|
||||
form: conj.imperfective.past,
|
||||
formula: "Imperfective Root + Past Ending",
|
||||
sentence: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
// - subj pastEquative (N && "not") v.2 obj
|
||||
`${engSubj(s)} ${engEquative("past", s)}${n ? " not" : ""} ${v[2]}`,
|
||||
// - subj "would" (N && "not") v.0 obj
|
||||
`${engSubj(s)} would${n ? " not" : ""} ${isToBe(v) ? "be" : v[0]}`,
|
||||
// - subj pastEquative (N && "not") going to" v.0 obj
|
||||
`${engSubj(s)} ${engEquative("past", s)}${n ? " not" : ""} going to ${isToBe(v) ? "be" : v[0]}`,
|
||||
]),
|
||||
explanation: "Saying something was happening, or would happen ('I was ____ing', 'I would ____')",
|
||||
past: true,
|
||||
},
|
||||
{
|
||||
label: "Simple Past",
|
||||
aspect: "perfective",
|
||||
form: conj.perfective.past,
|
||||
formula: "Perfective Root + Past Ending",
|
||||
sentence: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)}${isToBe(v)
|
||||
? ` ${engEquative("past", s)}${n ? " not" : ""}`
|
||||
: `${n ? " did not" : ""} ${v[3]}`}`,
|
||||
]),
|
||||
explanation: "Saying something happened ('I ____ed')",
|
||||
past: true,
|
||||
},
|
||||
{
|
||||
label: "Perfect",
|
||||
subgroup: "perfect",
|
||||
sentence: true,
|
||||
content: [
|
||||
{
|
||||
label: "Half Perfect",
|
||||
form: conj.perfect.halfPerfect,
|
||||
past: true,
|
||||
sentence: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} ${engHave(s)}${n ? " not" : ""} ${v[4]}`,
|
||||
]),
|
||||
formula: "Past participle inflected",
|
||||
secondPronounNeeded: true,
|
||||
explanation: "The base of all perfect forms. Used on it's own as a sort of abreviated form of the present perfect.",
|
||||
},
|
||||
{
|
||||
label: "Past Perfect",
|
||||
form: conj.perfect.past,
|
||||
past: true,
|
||||
sentence: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} had${n ? " not" : ""} ${v[4]}`,
|
||||
]),
|
||||
formula: "Past participle inflected + Past Equative",
|
||||
explanation: "Talking about events that had happened in the past, or had affected a past situation ('I had ____ed')",
|
||||
reorderWithNegative: true,
|
||||
},
|
||||
{
|
||||
label: "Present Perfect",
|
||||
form: conj.perfect.present,
|
||||
past: true,
|
||||
sentence: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} ${engHave(s)}${n ? " not" : ""} ${v[4]}`,
|
||||
]),
|
||||
formula: "Past participle inflected + Present Equative",
|
||||
explanation: "Talking about that something happened in the past and it affects the present ('I have _____ed')",
|
||||
reorderWithNegative: true,
|
||||
},
|
||||
{
|
||||
label: "Habitual Perfect",
|
||||
form: conj.perfect.habitual,
|
||||
past: true,
|
||||
sentence: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} ${engHave(s)}${n ? " not" : ""} ${v[4]}`,
|
||||
]),
|
||||
formula: "Past participle inflected + Habitual Equative",
|
||||
explanation: "Talking about something that will have happened habitually",
|
||||
reorderWithNegative: true,
|
||||
},
|
||||
{
|
||||
label: "Subjunctive Perfect",
|
||||
form: conj.perfect.subjunctive,
|
||||
past: true,
|
||||
sentence: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`that ${engSubj(s, true)} will have${n ? " not" : ""} ${v[4]}`,
|
||||
]),
|
||||
formula: "Past participle inflected + Subjunctive Equative",
|
||||
explanation: "expressing hope, desire, or judgement about an action having happened",
|
||||
reorderWithNegative: true,
|
||||
},
|
||||
{
|
||||
label: "Future/Presumptive Perfect",
|
||||
form: conj.perfect.future,
|
||||
advanced: true,
|
||||
past: true,
|
||||
sentence: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} will${n ? " not" : ""} have ${v[4]}`,
|
||||
]),
|
||||
formula: "به - ba + Past participle Inflected + Future Equative",
|
||||
explanation: "Talking about something that will have happened in the future, or guessing that the event will have occured presently ('I will have ____ed')",
|
||||
reorderWithNegative: true,
|
||||
},
|
||||
{
|
||||
label: "Affirmational Perfect",
|
||||
form: conj.perfect.affirmational,
|
||||
advanced: true,
|
||||
past: true,
|
||||
sentence: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} will${n ? " not" : ""} have ${v[4]}`,
|
||||
]),
|
||||
explanation: "Affirming that an event will have taken place ('I will have ____ed')",
|
||||
formula: "به - ba + Past Participle Inflected + Past Equative",
|
||||
reorderWithNegative: true,
|
||||
},
|
||||
{
|
||||
label: "Conterfactual/Past Subjunctive Perfect",
|
||||
form: conj.perfect.pastSubjunctiveHypothetical,
|
||||
advanced: true,
|
||||
past: true,
|
||||
sentence: true,
|
||||
secondPronounNeeded: true,
|
||||
explanation: "Talking about an event that would have hypothetically taken place (but didn't), or that should have taken place but didn't",
|
||||
formula: "به - ba + Past Participle Inflected + Past Subjunctive / Hypothetical Equative",
|
||||
reorderWithNegative: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Modal (ability/possibility)",
|
||||
subgroup: "modal",
|
||||
sentence: true,
|
||||
content: [
|
||||
{
|
||||
label: "Present Modal",
|
||||
aspect: "imperfective",
|
||||
form: conj.imperfective.modal.nonImperative,
|
||||
sentence: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} can${n ? "'t" : ""} ${isToBe(v) ? "be" : v[0]}`,
|
||||
]),
|
||||
formula: "Imperfective Root + Non-Inflectinig Ey-Tail + Subjunctive کېدل - to become",
|
||||
explanation: "saying that something is possible currently or in general ('I can ____')",
|
||||
reorderWithNegative: true,
|
||||
},
|
||||
{
|
||||
label: "Subjunctive Modal",
|
||||
aspect: "perfective",
|
||||
form: conj.perfective.modal.nonImperative,
|
||||
advanced: true,
|
||||
sentence: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`that ${engSubj(s, true)} can${n ? "'t" : ""} ${isToBe(v) ? "be" : v[0]}`,
|
||||
]),
|
||||
formula: "Perfective Root + Non-Inflectinig Ey-Tail + Subjunctive کېدل - to become",
|
||||
explanation: "talking about the possibility of something in a subjunctive way ('so that I can ____')",
|
||||
reorderWithNegative: true,
|
||||
},
|
||||
{
|
||||
label: "Imperfective Future Modal",
|
||||
aspect: "imperfective",
|
||||
form: conj.imperfective.modal.future,
|
||||
advanced: true,
|
||||
sentence: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} will${n ? " not" : ""} be able to ${isToBe(v) ? "be" : v[0]}`,
|
||||
]),
|
||||
formula: "به - ba + Present Modal",
|
||||
explanation: "saying that something will be possible in general or in an ongoing sense in the future ('I'll be able to ____')",
|
||||
reorderWithNegative: true,
|
||||
},
|
||||
{
|
||||
label: "Perfective Future Modal",
|
||||
aspect: "perfective",
|
||||
form: conj.perfective.modal.future,
|
||||
advanced: true,
|
||||
sentence: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} will${n ? " not" : ""} be able to ${isToBe(v) ? "be" : v[0]}`,
|
||||
]),
|
||||
formula: "به - ba + Subjunctive Modal",
|
||||
explanation: "saying that something will be possible at a certain point in the future ('I'll be able to ____')",
|
||||
reorderWithNegative: true,
|
||||
},
|
||||
{
|
||||
label: "Continous Past Modal",
|
||||
aspect: "imperfective",
|
||||
form: conj.imperfective.modal.past,
|
||||
advanced: true,
|
||||
past: true,
|
||||
sentence: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} ${engEquative("past", s)} ${n ? " not" : ""} able to ${isToBe(v) ? "be" : v[0]}`,
|
||||
`${engSubj(s)} could${n ? " not" : ""} ${v[0]}`,
|
||||
]),
|
||||
formula: "Imperfective Root + Non-Inflectinig Ey-Tail + Simple Past کېدل - to become",
|
||||
explanation: "saying that something was possible in general, in an ongoing sense ('I was able to ____', ie. 'I could do ____ any time')",
|
||||
reorderWithNegative: true,
|
||||
},
|
||||
{
|
||||
label: "Simple Past Modal",
|
||||
aspect: "perfective",
|
||||
form: conj.perfective.modal.past,
|
||||
formula: "Perfective Root + Non-Inflectinig Ey-Tail + Simple Past کېدل - to become",
|
||||
explanation: "saying that something was possible at a certain point in time ('I was able to ____, at one particular point in time')",
|
||||
past: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} ${engEquative("past", s)} ${n ? " not" : ""} able to ${isToBe(v) ? "be" : v[0]}`,
|
||||
`${engSubj(s)} could${n ? " not" : ""} ${isToBe(v) ? "be" : v[0]}`,
|
||||
]),
|
||||
sentence: true,
|
||||
advanced: true,
|
||||
reorderWithNegative: true,
|
||||
},
|
||||
{
|
||||
label: "Imperfective hypothetical/wildcard Past Modal",
|
||||
aspect: "imperfective",
|
||||
form: conj.imperfective.modal.hypotheticalPast,
|
||||
formula: "Imperfective Root + Non-Inflectinig Ey-Tail + ش - sh + Non-Inflectinig Ey-Tail",
|
||||
explanation: "saying that something was possible in general, in an ongoing sense ('I was able to ____', ie. 'I could do ____ any time'). This 'wildcard' form can be used either to talk about hypothetical things, or to avoid worrying about verb agreement",
|
||||
past: true,
|
||||
sentence: true,
|
||||
advanced: true,
|
||||
reorderWithNegative: true,
|
||||
},
|
||||
{
|
||||
label: "Perfective hypothetical/wildcard Past Modal",
|
||||
aspect: "perfective",
|
||||
form: conj.perfective.modal.hypotheticalPast,
|
||||
formula: "Perfective Root + Non-Inflectinig Ey-Tail + ش - sh + Non-Inflectinig Ey-Tail",
|
||||
explanation: "saying that something was possible at a certain point in time ('I was able to ____, at one particular point in time'). This 'wildcard' form can be used either to talk about hypothetical things, or to avoid worrying about verb agreement",
|
||||
past: true,
|
||||
sentence: true,
|
||||
advanced: true,
|
||||
reorderWithNegative: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Hypothetical/Wish",
|
||||
advanced: true,
|
||||
form: conj.hypothetical,
|
||||
formula: "Imperfective Root + Non-Inflecting Ey-Tail",
|
||||
explanation: "Talking about a hypothetical, unreal situation, or something that is wished for ('If I ____')",
|
||||
past: true,
|
||||
},
|
||||
{
|
||||
label: "Participle",
|
||||
subgroup: "participle",
|
||||
advanced: true,
|
||||
content: [
|
||||
{
|
||||
label: "Present Participle",
|
||||
form: conj.participle.present,
|
||||
formula: "Short form of Ininitive Root + ونکی - oonkey",
|
||||
explanation: "Making a verb into a noun or adjective, talking about a person or thing that does or experiences something. Also used to say something is about to happen. ('____ing', '____er')",
|
||||
},
|
||||
{
|
||||
label: "Past Participle",
|
||||
form: conj.participle.past,
|
||||
past: true,
|
||||
formula: "Infinitive Root or Special Form + Inflecting Ey-Tail",
|
||||
explanation: "Making a verb into a noun or adjective, talking about how a person or thing did or experienced something. ('____ed')",
|
||||
},
|
||||
],
|
||||
},
|
||||
...conj.passive ?
|
||||
[{
|
||||
label: "Passive",
|
||||
subgroup: "passive",
|
||||
advanced: true,
|
||||
sentence: true,
|
||||
content: [
|
||||
{
|
||||
label: "Passive Present",
|
||||
aspect: "imperfective",
|
||||
form: conj.passive.imperfective.nonImperative,
|
||||
sentence: true,
|
||||
passive: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} ${engEquative("present", s)}${n ? " not" : ""} being ${v[4]}`,
|
||||
]),
|
||||
formula: "Long Imperfective Root + Present کېدل - to become",
|
||||
explanation: "Saying that something is being done or is done in general, without mentioning the subject/agent. ('I am being ____en')",
|
||||
},
|
||||
{
|
||||
label: "Passive Subjunctive",
|
||||
aspect: "perfective",
|
||||
form: conj.passive.perfective.nonImperative,
|
||||
sentence: true,
|
||||
passive: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`that ${engSubj(s, true)} will${n ? " not" : ""} be ${v[4]}`,
|
||||
]),
|
||||
formula: "Long Perfective Root + Subjunctive کېدل - to become",
|
||||
explanation: "Saying that something should be done, or giving a purpose for something being done etc., without mentioning the subject/agent. ('Should I be ____en?', 'So that I'll be ____en')"
|
||||
},
|
||||
{
|
||||
label: "Passive Imperfective Future",
|
||||
aspect: "imperfective",
|
||||
form: conj.passive.imperfective.future,
|
||||
sentence: true,
|
||||
passive: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} will${n ? " not" : ""} be ${v[4]}`,
|
||||
]),
|
||||
formula: "به - ba + Passive Present",
|
||||
explanation: "Saying something will be done as a one-time event, without mentioning the subject/agent.",
|
||||
},
|
||||
{
|
||||
label: "Passive Perfective Future",
|
||||
aspect: "perfective",
|
||||
form: conj.passive.perfective.future,
|
||||
sentence: true,
|
||||
passive: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} will${n ? " not" : ""} be ${v[4]}`,
|
||||
]),
|
||||
formula: "به - ba + Passive Subjunctive",
|
||||
explanation: "Saying something will be done in an ongoing or repeated sense, without mentioning the subject/agent."
|
||||
},
|
||||
{
|
||||
label: "Passive Continuous Past",
|
||||
aspect: "imperfective",
|
||||
form: conj.passive.imperfective.past,
|
||||
past: true,
|
||||
sentence: true,
|
||||
passive: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} ${engEquative("past", s)}${n ? " not" : ""} being ${v[4]}`,
|
||||
]),
|
||||
formula: "Long Imperfective Root + Continuous Past کېدل - to become",
|
||||
explanation: "Saying that something was being done, or would be done, without mentioning the subject/agent. ('I was being ____en', 'I would be ____en')",
|
||||
},
|
||||
{
|
||||
label: "Passive Simple Past",
|
||||
aspect: "perfective",
|
||||
form: conj.passive.perfective.past,
|
||||
past: true,
|
||||
sentence: true,
|
||||
passive: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} ${engEquative("past", s)}${n ? " not" : ""} ${v[4]}`,
|
||||
]),
|
||||
formula: "Long Perfective Root + Simple Past کېدل - to become",
|
||||
explanation: "Saying that was done as a one-time event, without mentioning the subject/agent. ('I was ____en')"
|
||||
},
|
||||
{
|
||||
label: "Passive Perfect",
|
||||
subgroup: "passive perfect",
|
||||
passive: true,
|
||||
sentence: true,
|
||||
content: [
|
||||
{
|
||||
label: "Passive Half Perfect",
|
||||
form: conj.passive.perfect.halfPerfect,
|
||||
past: true,
|
||||
sentence: true,
|
||||
passive: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} ${engHave(s)}${n ? " not" : ""} been ${v[4]}`,
|
||||
]),
|
||||
formula: "Infinitive + کېدل past participle inflected",
|
||||
explanation: "The base of all perfect forms. Used on it's own as a sort of abbreviated form of the present perfect. (Passive voice)",
|
||||
},
|
||||
{
|
||||
label: "Passive Past Perfect",
|
||||
form: conj.passive.perfect.past,
|
||||
past: true,
|
||||
sentence: true,
|
||||
passive: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} had${n ? " not" : ""} been ${v[4]}`,
|
||||
]),
|
||||
formula: "Infinitive + کېدل past participle inflected + Past Equative",
|
||||
explanation: "Talking about events that had happened in the past, or had affected a past situation (Passive voice) ('I had been ____ed')",
|
||||
},
|
||||
{
|
||||
label: "Passive Present Perfect",
|
||||
form: conj.passive.perfect.present,
|
||||
past: true,
|
||||
sentence: true,
|
||||
passive: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} ${engHave(s)}${n ? " not" : ""} been ${v[4]}`,
|
||||
]),
|
||||
formula: "Infinitive + کېدل past participle inflected + Present Equative",
|
||||
explanation: "Talking about that something happened in the past and it affects the present (Passive voice) ('I have been _____ed')",
|
||||
},
|
||||
{
|
||||
label: "Passive Habitual Perfect",
|
||||
form: conj.passive.perfect.subjunctive,
|
||||
past: true,
|
||||
sentence: true,
|
||||
passive: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} ${engHave(s)}${n ? " not" : ""} been ${v[4]}`,
|
||||
]),
|
||||
formula: "Infinitive + کېدل past participle inflected + Habitual Equative",
|
||||
},
|
||||
{
|
||||
label: "Passive Subjunctive Perfect",
|
||||
form: conj.passive.perfect.subjunctive,
|
||||
past: true,
|
||||
sentence: true,
|
||||
passive: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`that ${engSubj(s, true)} will${n ? " not" : ""} have been ${v[4]}`,
|
||||
]),
|
||||
formula: "Infinitive + کېدل past participle inflected + Subjunctive Equative",
|
||||
},
|
||||
{
|
||||
label: "Passive Future/Presumptive Perfect",
|
||||
form: conj.passive.perfect.future,
|
||||
advanced: true,
|
||||
past: true,
|
||||
sentence: true,
|
||||
passive: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} will${n ? " not" : ""} have been ${v[4]}`,
|
||||
]),
|
||||
formula: "به - ba + Infinitive + کېدل past participle inflected + Future Equative",
|
||||
explanation: "Talking about something that will have happened in the future, or guessing that the event will have occured presently (Passive voice) ('I will have been ____ed')",
|
||||
},
|
||||
{
|
||||
label: "Passive Affirmational Perfect",
|
||||
form: conj.passive.perfect.affirmational,
|
||||
advanced: true,
|
||||
past: true,
|
||||
sentence: true,
|
||||
passive: true,
|
||||
englishBuilder: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
|
||||
`${engSubj(s)} will${n ? " not" : ""} have been ${v[4]}`,
|
||||
]),
|
||||
explanation: "Affirming that an event will have taken place (Passive voice) ('I will have been ____ed')",
|
||||
formula: "به - ba + Infinitive + کېدل past participle inflected + Past Equative"
|
||||
},
|
||||
{
|
||||
label: "Passive Past Subjunctive / Hypothetical Perfect",
|
||||
form: conj.passive.perfect.pastSubjunctiveHypothetical,
|
||||
advanced: true,
|
||||
past: true,
|
||||
sentence: true,
|
||||
passive: true,
|
||||
explanation: "Talking about an event that would have hypothetically taken place, or that should have taken place (Passive voice) ('I would have been ____ed')",
|
||||
formula: "به - ba + Infinitive + کېدل past participle inflected + Past Subjunctive / Hypothetical Equative"
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
} as T.DisplayFormSubgroup]
|
||||
: [],
|
||||
];
|
||||
|
||||
export const getForms = ({ conj, filterFunc, mode, subject, object, sentenceLevel, englishConjugation, negative }: {
|
||||
conj: T.VerbConjugation,
|
||||
englishConjugation?: T.EnglishVerbConjugation
|
||||
filterFunc?: FilterFunc | FilterFunc[],
|
||||
mode: "chart" | "sentence",
|
||||
subject: T.Person,
|
||||
object: T.Person,
|
||||
sentenceLevel?: "easy" | "medium" | "hard",
|
||||
negative: boolean,
|
||||
}): T.DisplayFormItem[] => {
|
||||
const forms = formsOfConjugation(conj);
|
||||
const formsToUse = filterFunc
|
||||
? formFilter(forms, filterFunc)
|
||||
: forms;
|
||||
return mode === "chart"
|
||||
? formsToUse
|
||||
: formMap(
|
||||
formFilter(formsToUse, (f) => f.sentence),
|
||||
makeSentence,
|
||||
conj.info,
|
||||
subject,
|
||||
object,
|
||||
negative,
|
||||
englishConjugation,
|
||||
sentenceLevel,
|
||||
);
|
||||
}
|
||||
|
||||
function isThirdPersonSing(p: T.Person): boolean {
|
||||
return (
|
||||
p === T.Person.ThirdSingMale ||
|
||||
p === T.Person.ThirdSingFemale
|
||||
);
|
||||
}
|
||||
|
||||
function engPresC(s: T.Person, ec: T.EnglishVerbConjugationEc | [string, string]): string {
|
||||
return isThirdPersonSing(s) ? ec[1] : ec[0];
|
||||
}
|
||||
|
||||
function engEquative(tense: "past" | "present", s: T.Person): string {
|
||||
const [row, col] = getVerbBlockPosFromPerson(s);
|
||||
return englishEquative[tense][row][col];
|
||||
}
|
||||
|
||||
function engHave(s: T.Person): string {
|
||||
return isThirdPersonSing(s) ? "has" : "have";
|
||||
}
|
||||
|
||||
function engSubj(s: T.Person, lowerCase?: boolean): string {
|
||||
const pronoun = (s === T.Person.FirstSingMale || s === T.Person.FirstSingFemale)
|
||||
? "I"
|
||||
: (s === T.Person.FirstPlurMale || s === T.Person.FirstPlurFemale)
|
||||
? "We"
|
||||
: (s === T.Person.SecondSingMale || s === T.Person.SecondSingFemale)
|
||||
? "You"
|
||||
: (s === T.Person.SecondPlurMale || s === T.Person.SecondPlurFemale)
|
||||
? "You (pl.)"
|
||||
: (s === T.Person.ThirdSingMale)
|
||||
? "He/it"
|
||||
: (s === T.Person.ThirdSingFemale)
|
||||
? "She/it"
|
||||
: (s === T.Person.ThirdPlurMale)
|
||||
? "They"
|
||||
: "They (f.)";
|
||||
return (lowerCase && pronoun !== "I")
|
||||
? pronoun.toLowerCase()
|
||||
: pronoun;
|
||||
}
|
|
@ -9,6 +9,14 @@
|
|||
import { kawulStat } from "./irregular-conjugations";
|
||||
import * as T from "../types";
|
||||
|
||||
export const negativeParticle: {
|
||||
imperative: T.PsString,
|
||||
nonImperative: T.PsString,
|
||||
} = {
|
||||
nonImperative: { p: "نه", f: "nú" },
|
||||
imperative: { p: "مه", f: "mú" },
|
||||
};
|
||||
|
||||
export const presentEndings: T.VerbBlock = [
|
||||
[
|
||||
[{
|
||||
|
|
|
@ -687,6 +687,21 @@ export function uniquePsStringArray(arr: T.PsString[]): T.PsString[] {
|
|||
].map((string) => JSON.parse(string)) as T.PsString[];
|
||||
}
|
||||
|
||||
export function splitOffLeapfrogWordFull(ps: T.SingleOrLengthOpts<T.PsString[]>): [T.SingleOrLengthOpts<T.PsString[]>, T.SingleOrLengthOpts<T.PsString[]>] {
|
||||
if ("long" in ps) {
|
||||
const [shortA, shortB] = splitOffLeapfrogWordFull(ps.short) as [T.PsString[], T.PsString[]];
|
||||
const [longA, longB] = splitOffLeapfrogWordFull(ps.long) as [T.PsString[], T.PsString[]];
|
||||
return [{ long: longA, short: shortA }, { long: longB, short: shortB }];
|
||||
}
|
||||
return ps.reduce((accum, curr): [T.PsString[], T.PsString[]] => {
|
||||
const [front, back] = splitOffLeapfrogWord(curr);
|
||||
return [
|
||||
[...accum[0], front],
|
||||
[...accum[1], back],
|
||||
];
|
||||
}, [[], []] as [T.PsString[], T.PsString[]])
|
||||
}
|
||||
|
||||
export function splitOffLeapfrogWord(ps: T.PsString): [T.PsString, T.PsString] {
|
||||
const pWords = ps.p.split(" ");
|
||||
const fWords = ps.f.split(" ");
|
||||
|
@ -984,8 +999,12 @@ export function psStringFromEntry(entry: T.PsString): T.PsString {
|
|||
};
|
||||
}
|
||||
|
||||
export function getLength<U>(x: T.SingleOrLengthOpts<U>, length: "long" | "short"): U {
|
||||
return ("long" in x) ? x[length] : x;
|
||||
export function getLength<U>(x: T.SingleOrLengthOpts<U>, length: "long" | "short" | "mini"): U {
|
||||
if ("long" in x) {
|
||||
const s = x[length];
|
||||
return s ? s : x.short;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
export function getLong<U>(x: T.SingleOrLengthOpts<U>): U {
|
||||
|
|
|
@ -11,24 +11,61 @@ export function getSubjectSelection(blocks: T.EPSBlock[] | T.EPSBlockComplete[]
|
|||
return b.block;
|
||||
}
|
||||
|
||||
export function getSubjectSelectionFromBlocks(blocks: T.Block[]): T.Rendered<T.SubjectSelectionComplete> {
|
||||
const b = blocks.find(f => f.type === "subjectSelection");
|
||||
export function getSubjectSelectionFromBlocks(blocks: T.Block[][]): T.Rendered<T.SubjectSelectionComplete> {
|
||||
const b = blocks[0].find(f => f.type === "subjectSelection");
|
||||
if (!b || b.type !== "subjectSelection") {
|
||||
throw new Error("subjectSelection not found in blocks");
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
export function getPredicateSelectionFromBlocks(blocks: T.Block[]): T.Rendered<T.PredicateSelectionComplete> {
|
||||
const b = blocks.find(f => f.type === "predicateSelection");
|
||||
export function getObjectSelectionFromBlocks(blocks: T.Block[][]): T.Rendered<T.ObjectSelectionComplete> {
|
||||
const b = blocks[0].find(f => f.type === "objectSelection");
|
||||
if (!b || b.type !== "objectSelection") {
|
||||
throw new Error("objectSelection not found in blocks");
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
export function getVerbFromBlocks(blocks: T.Block[][]): T.VerbRenderedBlock {
|
||||
const b = blocks[0].find(f => f.type === "verb");
|
||||
const p = blocks[0].find(f => f.type === "perfectParticipleBlock");
|
||||
const m = blocks[0].find(f => f.type === "modalVerbBlock");
|
||||
const v = (b && b.type === "verb")
|
||||
? b
|
||||
: (p && p.type === "perfectParticipleBlock")
|
||||
? p.verb
|
||||
: (m && m.type === "modalVerbBlock")
|
||||
? m.verb
|
||||
: undefined;
|
||||
if (!v) {
|
||||
throw new Error("verbSelection not found in blocks");
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
export function getVerbAndHeadFromBlocks(blocks: T.Block[][]): { verb: T.VerbRenderedBlock, perfectiveHead: T.PerfectiveHeadBlock } {
|
||||
const verb = getVerbFromBlocks(blocks);
|
||||
const perfectiveHead = blocks[0].find(f => f.type === "perfectiveHead");
|
||||
if (!perfectiveHead || perfectiveHead.type !== "perfectiveHead") {
|
||||
throw new Error("perfectiveHead not found in blocks");
|
||||
}
|
||||
return {
|
||||
verb,
|
||||
perfectiveHead,
|
||||
};
|
||||
}
|
||||
|
||||
export function getPredicateSelectionFromBlocks(blocks: T.Block[][]): T.Rendered<T.PredicateSelectionComplete> {
|
||||
const b = blocks[0].find(f => f.type === "predicateSelection");
|
||||
if (!b || b.type !== "predicateSelection") {
|
||||
throw new Error("predicateSelection not found in blocks");
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
export function getAPsFromBlocks(blocks: T.Block[]): T.Rendered<T.APSelection>[] {
|
||||
return blocks.filter(b => b.type === "AP") as T.Rendered<T.APSelection>[];
|
||||
export function getAPsFromBlocks(blocks: T.Block[][]): T.Rendered<T.APSelection>[] {
|
||||
return blocks[0].filter(b => b.type === "AP") as T.Rendered<T.APSelection>[];
|
||||
}
|
||||
|
||||
export function getObjectSelection(blocks: T.VPSBlockComplete[]): T.ObjectSelectionComplete;
|
||||
|
@ -41,23 +78,6 @@ export function getObjectSelection(blocks: T.VPSBlock[] | T.VPSBlockComplete[]):
|
|||
return b.block;
|
||||
}
|
||||
|
||||
export function getRenderedSubjectSelection(blocks: (T.Rendered<T.SubjectSelectionComplete> | T.Rendered<T.NPSelection> | T.Rendered<T.APSelection> | T.RenderedVPSBlock)[]): T.Rendered<T.SubjectSelectionComplete> {
|
||||
const b = blocks.find(f => typeof f === "object" && f.type === "subjectSelection");
|
||||
if (!b || typeof b !== "object" || b.type !== "subjectSelection") {
|
||||
throw new Error("subjectSelection not found in blocks");
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
export function getRenderedObjectSelection(blocks: T.RenderedVPSBlock[]): T.Rendered<T.ObjectSelectionComplete> {
|
||||
const b = blocks.find(f => typeof f === "object" && f.type === "objectSelection");
|
||||
if (!b || typeof b !== "object" || b.type !== "objectSelection") {
|
||||
throw new Error("objectSelection not found in blocks");
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
export function makeEPSBlocks(): T.EPSBlock[] {
|
||||
return [
|
||||
{
|
||||
|
@ -206,7 +226,8 @@ export function isNoObject(b: T.VPSBlock["block"] | T.EPSBlock["block"]): b is {
|
|||
return !!(b && b.type === "objectSelection" && b.selection === "none");
|
||||
}
|
||||
|
||||
export function specifyEquativeLength(blocks: T.Block[], length: "long" | "short"): T.Block[] {
|
||||
export function specifyEquativeLength(blocksWVars: T.Block[][], length: "long" | "short"): T.Block[][] {
|
||||
function specify(blocks: T.Block[]): T.Block[] {
|
||||
const i = blocks.findIndex(b => b.type === "equative");
|
||||
if (i === -1) throw new Error("equative block not found in EPRendered");
|
||||
const eq = blocks[i];
|
||||
|
@ -221,14 +242,61 @@ export function specifyEquativeLength(blocks: T.Block[], length: "long" | "short
|
|||
};
|
||||
return adjusted;
|
||||
}
|
||||
return blocksWVars.map(specify);
|
||||
}
|
||||
|
||||
export function hasEquativeWithLengths(blocks: T.Block[]): boolean {
|
||||
const equative = blocks.find(x => x.type === "equative");
|
||||
export function specifyVerbLength(blocksWVars: T.Block[][], length: "long" | "short" | "mini"): T.Block[][] {
|
||||
function specify(blocks: T.Block[]): T.Block[] {
|
||||
return blocks.map((block) => {
|
||||
if (block.type === "verb") {
|
||||
const v: T.VerbRenderedBlock = {
|
||||
...block,
|
||||
block: {
|
||||
...block.block,
|
||||
ps: getLength(block.block.ps, length),
|
||||
},
|
||||
};
|
||||
return v;
|
||||
}
|
||||
if (block.type === "perfectParticipleBlock") {
|
||||
const p: T.PerfectParticipleBlock = {
|
||||
...block,
|
||||
ps: getLength(block.ps, length),
|
||||
};
|
||||
return p;
|
||||
}
|
||||
if (block.type === "modalVerbBlock") {
|
||||
const m: T.ModalVerbBlock = {
|
||||
...block,
|
||||
ps: getLength(block.ps, length),
|
||||
};
|
||||
return m;
|
||||
}
|
||||
return block;
|
||||
});
|
||||
}
|
||||
return blocksWVars.map(specify);
|
||||
}
|
||||
|
||||
export function hasEquativeWithLengths(blocks: T.Block[][]): boolean {
|
||||
const equative = blocks[0].find(x => x.type === "equative");
|
||||
if (!equative) throw new Error("equative not found in blocks");
|
||||
if (equative.type !== "equative") throw new Error("error finding equative in blocks");
|
||||
return "long" in equative.equative.ps;
|
||||
}
|
||||
|
||||
export function hasVerbWithLengths(blocks: T.Block[][]): boolean {
|
||||
// TODO: handle length options with perfect verb equative as well?
|
||||
const verb = blocks[0].find(x => (x.type === "verb" || x.type === "perfectParticipleBlock" || x.type === "modalVerbBlock"));
|
||||
if (!verb) throw new Error("verb not found in blocks");
|
||||
if (verb.type !== "verb" && verb.type !== "perfectParticipleBlock" && verb.type !== "modalVerbBlock") throw new Error("error finding verb in blocks");
|
||||
return (
|
||||
(verb.type === "verb" && "long" in verb.block.ps)
|
||||
|| (verb.type === "perfectParticipleBlock" && "long" in verb.ps)
|
||||
|| (verb.type === "modalVerbBlock" && "long" in verb.ps)
|
||||
);
|
||||
}
|
||||
|
||||
function arrayMove<X>(ar: X[], old_index: number, new_index: number): X[] {
|
||||
const arr = [...ar];
|
||||
const new_i = (new_index >= arr.length)
|
||||
|
|
|
@ -1,33 +1,27 @@
|
|||
import * as T from "../../types";
|
||||
import {
|
||||
concatPsString, getLong,
|
||||
concatPsString, getLong, getShort,
|
||||
} from "../p-text-helpers";
|
||||
import {
|
||||
Segment,
|
||||
makeSegment,
|
||||
flattenLengths,
|
||||
combineSegments,
|
||||
splitOffLeapfrogWord,
|
||||
putKidsInKidsSection as oldPutKidsInKidsSection,
|
||||
} from "./segment";
|
||||
import {
|
||||
removeAccents,
|
||||
} from "../accent-helpers";
|
||||
import { negativeParticle } from "../../lib/grammar-units";
|
||||
import * as grammarUnits from "../grammar-units";
|
||||
import {
|
||||
removeBa,
|
||||
removeDuplicates,
|
||||
} from "./vp-tools";
|
||||
import { isImperativeTense, isModalTense, isPerfectTense } from "../type-predicates";
|
||||
import { getEnglishFromRendered, getPashtoFromRendered } from "./np-tools";
|
||||
import { getVerbBlockPosFromPerson } from "../misc-helpers";
|
||||
import { pronouns } from "../grammar-units";
|
||||
import { completeEPSelection, renderEP } from "./render-ep";
|
||||
import { completeVPSelection } from "./vp-tools";
|
||||
import { renderVP } from "./render-vp";
|
||||
import { getAPsFromBlocks, getPredicateSelectionFromBlocks, getRenderedObjectSelection, getRenderedSubjectSelection, getSubjectSelectionFromBlocks, hasEquativeWithLengths, specifyEquativeLength } from "./blocks-utils";
|
||||
|
||||
// TODO: GET BLANKING WORKING!
|
||||
import {
|
||||
getAPsFromBlocks,
|
||||
getObjectSelectionFromBlocks,
|
||||
getPredicateSelectionFromBlocks,
|
||||
getSubjectSelectionFromBlocks,
|
||||
getVerbFromBlocks,
|
||||
hasEquativeWithLengths,
|
||||
hasVerbWithLengths,
|
||||
specifyEquativeLength,
|
||||
specifyVerbLength,
|
||||
} from "./blocks-utils";
|
||||
|
||||
const blank: T.PsString = {
|
||||
p: "______",
|
||||
|
@ -37,228 +31,31 @@ type BlankoutOptions = { equative?: boolean, ba?: boolean, kidsSection?: boolean
|
|||
|
||||
const kidsBlank: T.PsString = { p: "___", f: "___" };
|
||||
|
||||
export function compileVP(VP: T.VPRendered, form: T.FormVersion): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] };
|
||||
export function compileVP(VP: T.VPRendered, form: T.FormVersion, combineLengths: true): { ps: T.PsString[], e?: string [] };
|
||||
export function compileVP(VP: T.VPRendered, form: T.FormVersion, combineLengths?: true): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] } {
|
||||
const verb = VP.verb.ps;
|
||||
const { kids, blocks } = getVPSegmentsAndKids(VP, form);
|
||||
const psResult = compilePs({
|
||||
blocks,
|
||||
kids,
|
||||
verb,
|
||||
VP,
|
||||
});
|
||||
return {
|
||||
ps: combineLengths ? flattenLengths(psResult) : psResult,
|
||||
// TODO: English doesn't quite work for dynamic compounds in passive voice
|
||||
e: (VP.verb.voice === "passive" && VP.isCompound === "dynamic") ? undefined : compileEnglishVP(VP),
|
||||
};
|
||||
}
|
||||
|
||||
type CompilePsInput = {
|
||||
blocks: Segment[],
|
||||
kids: Segment[],
|
||||
verb: {
|
||||
head: T.PsString | undefined,
|
||||
rest: T.SingleOrLengthOpts<T.PsString[]>,
|
||||
},
|
||||
VP: T.VPRendered,
|
||||
}
|
||||
// function compilePs({ blocks, kids, verb: { head, rest }, VP }: CompilePsInput): T.SingleOrLengthOpts<T.PsString[]> {
|
||||
// if ("long" in rest) {
|
||||
// return {
|
||||
// long: compilePs({ blocks, verb: { head, rest: rest.long }, VP, kids }) as T.PsString[],
|
||||
// short: compilePs({ blocks, verb: { head, rest: rest.short }, VP, kids }) as T.PsString[],
|
||||
// ...rest.mini ? {
|
||||
// mini: compilePs({ blocks, verb: { head, rest: rest.mini }, VP, kids }) as T.PsString[],
|
||||
// } : {},
|
||||
// };
|
||||
// }
|
||||
// const verbWNegativeVersions = arrangeVerbWNegative(head, rest, VP.verb);
|
||||
|
||||
function compilePs({ blocks, kids, verb: { head, rest }, VP }: CompilePsInput): T.SingleOrLengthOpts<T.PsString[]> {
|
||||
if ("long" in rest) {
|
||||
return {
|
||||
long: compilePs({ blocks, verb: { head, rest: rest.long }, VP, kids }) as T.PsString[],
|
||||
short: compilePs({ blocks, verb: { head, rest: rest.short }, VP, kids }) as T.PsString[],
|
||||
...rest.mini ? {
|
||||
mini: compilePs({ blocks, verb: { head, rest: rest.mini }, VP, kids }) as T.PsString[],
|
||||
} : {},
|
||||
};
|
||||
}
|
||||
const verbWNegativeVersions = arrangeVerbWNegative(head, rest, VP.verb);
|
||||
|
||||
// put together all the different possible permutations based on:
|
||||
// a. potential different versions of where the nu goes
|
||||
return removeDuplicates(verbWNegativeVersions.flatMap((verbSegments) => {
|
||||
// for each permutation of the possible ordering of NPs and Verb + nu
|
||||
// 1. put in kids in the kids section
|
||||
const segments = oldPutKidsInKidsSection([...blocks, ...verbSegments], kids);
|
||||
// 2. space out the words properly
|
||||
const withProperSpaces = addSpacesBetweenSegments(segments);
|
||||
// 3. throw it all together into a PsString for each permutation
|
||||
return combineSegments(withProperSpaces);
|
||||
}));
|
||||
}
|
||||
|
||||
export function getShrunkenServant(VP: T.VPRendered): Segment | undefined {
|
||||
const shrinkServant = VP.form.shrinkServant && !(VP.isCompound === "dynamic" && !VP.isPast);
|
||||
const toShrinkServant: T.Rendered<T.NPSelection> | undefined = (() => {
|
||||
if (!shrinkServant) return undefined;
|
||||
if (!VP.servant) return undefined;
|
||||
const servant = VP.servant === "subject"
|
||||
? getRenderedSubjectSelection(VP.blocks).selection
|
||||
: getRenderedObjectSelection(VP.blocks).selection;
|
||||
if (typeof servant !== "object") return undefined;
|
||||
return servant;
|
||||
})();
|
||||
return toShrinkServant ? shrinkNP(toShrinkServant) : undefined;
|
||||
}
|
||||
|
||||
export function getVPSegmentsAndKids(VP: T.VPRendered, form?: T.FormVersion): { kids: Segment[], blocks: Segment[] } {
|
||||
const removeKing = VP.form.removeKing && !(VP.isCompound === "dynamic" && VP.isPast);
|
||||
const shrunkenServant = getShrunkenServant(VP);
|
||||
const possToShrink = findPossesivesToShrinkInVP(VP, {
|
||||
shrunkServant: !!shrunkenServant,
|
||||
removedKing: removeKing,
|
||||
});
|
||||
const subject = getRenderedSubjectSelection(VP.blocks).selection;
|
||||
const blocks = VP.blocks.reduce((accum, block) => {
|
||||
if (block.type === "subjectSelection") {
|
||||
if (VP.servant === "subject" && shrunkenServant) return accum;
|
||||
if (VP.king === "subject" && removeKing) return accum;
|
||||
return [
|
||||
...accum,
|
||||
makeSegment(getPashtoFromRendered(block.selection, false)),
|
||||
];
|
||||
}
|
||||
if (block.type === "objectSelection") {
|
||||
if (VP.servant === "object" && shrunkenServant) return accum;
|
||||
if (VP.king === "object" && removeKing) return accum;
|
||||
if (typeof block.selection !== "object") return accum;
|
||||
return [
|
||||
...accum,
|
||||
makeSegment(getPashtoFromRendered(block.selection, subject.selection.person)),
|
||||
]
|
||||
}
|
||||
return [
|
||||
...accum,
|
||||
makeSegment(getPashtoFromRendered(block, false)),
|
||||
];
|
||||
}, [] as Segment[]);
|
||||
return {
|
||||
kids: orderKidsSection([
|
||||
...VP.verb.hasBa
|
||||
? [makeSegment(grammarUnits.baParticle, ["isBa", "isKid"])] : [],
|
||||
...shrunkenServant
|
||||
? [shrunkenServant] : [],
|
||||
...possToShrink.map(shrinkNP),
|
||||
]),
|
||||
blocks: blocks,
|
||||
};
|
||||
}
|
||||
|
||||
function arrangeVerbWNegative(head: T.PsString | undefined, restRaw: T.PsString[], V: T.VerbRendered): Segment[][] {
|
||||
const hasLeapfrog = isPerfectTense(V.tense) || isModalTense(V.tense);
|
||||
const rest = (() => {
|
||||
if (hasLeapfrog) {
|
||||
const [restF, restLast] = splitOffLeapfrogWord(restRaw);
|
||||
return {
|
||||
front: makeSegment(restF.map(removeBa), ["isVerbRest"]),
|
||||
last: makeSegment(restLast.map(removeBa), ["isVerbRest"]),
|
||||
};
|
||||
}
|
||||
return makeSegment(restRaw.map(removeBa), ["isVerbRest"]);
|
||||
})();
|
||||
const headSegment: Segment | undefined = !head
|
||||
? head
|
||||
: makeSegment(
|
||||
head,
|
||||
(head.p === "و" || head.p === "وا")
|
||||
? ["isVerbHead", "isOoOrWaaHead"]
|
||||
: ["isVerbHead"]
|
||||
);
|
||||
if (!V.negative) {
|
||||
if ("front" in rest) {
|
||||
return [
|
||||
headSegment ? [headSegment, rest.front, rest.last] : [rest.front, rest.last],
|
||||
]
|
||||
}
|
||||
return [
|
||||
headSegment ? [headSegment, rest] : [rest],
|
||||
];
|
||||
}
|
||||
const nu: T.PsString = isImperativeTense(V.tense)
|
||||
? { p: "مه", f: "mú" }
|
||||
: { p: "نه", f: "nú" };
|
||||
if (!headSegment) {
|
||||
if ("front" in rest) {
|
||||
return [
|
||||
// pefect nu dey me leeduley and nu me dey leeduley
|
||||
// actually don't think this is correct - keeping it out for now
|
||||
// [
|
||||
// mergeSegments(
|
||||
// makeSegment(nu, ["isNu"]),
|
||||
// rest.last.adjust({ ps: removeAccents }),
|
||||
// ),
|
||||
// rest.front.adjust({ ps: removeAccents }),
|
||||
// ],
|
||||
[
|
||||
makeSegment(nu, ["isNu"]),
|
||||
rest.last.adjust({ ps: removeAccents }),
|
||||
rest.front.adjust({ ps: removeAccents }),
|
||||
],
|
||||
[
|
||||
rest.front.adjust({ ps: removeAccents }),
|
||||
makeSegment(nu, ["isNu"]),
|
||||
rest.last.adjust({ ps: removeAccents }),
|
||||
],
|
||||
];
|
||||
}
|
||||
return [[
|
||||
makeSegment(nu, ["isNu"]),
|
||||
rest.adjust({ ps: removeAccents }),
|
||||
]];
|
||||
}
|
||||
if ("front" in rest) {
|
||||
return [
|
||||
[
|
||||
headSegment.adjust({ ps: removeAccents }),
|
||||
rest.last.adjust({
|
||||
ps: r => concatPsString(nu, " ", removeAccents(r)),
|
||||
desc: ["isNu"],
|
||||
}),
|
||||
rest.front.adjust({
|
||||
ps: r => removeAccents(r),
|
||||
}),
|
||||
],
|
||||
[
|
||||
headSegment.adjust({ ps: removeAccents }),
|
||||
rest.front.adjust({
|
||||
ps: r => concatPsString(nu, " ", removeAccents(r)),
|
||||
desc: ["isNu"],
|
||||
}),
|
||||
rest.last.adjust({
|
||||
ps: r => removeAccents(r),
|
||||
}),
|
||||
],
|
||||
...(!headSegment.isOoOrWaaHead && !V.isCompound) ? [[
|
||||
mergeSegments(headSegment, rest.front, "no space").adjust({
|
||||
ps: r => concatPsString(nu, " ", removeAccents(r)),
|
||||
desc: ["isNu"],
|
||||
}),
|
||||
rest.last.adjust({
|
||||
ps: r => removeAccents(r),
|
||||
}),
|
||||
]] : [],
|
||||
];
|
||||
}
|
||||
return [
|
||||
...(V.voice !== "passive") ? [[
|
||||
...headSegment ? [headSegment.adjust({ ps: removeAccents })] : [],
|
||||
rest.adjust({
|
||||
ps: r => concatPsString(nu, " ", removeAccents(r)),
|
||||
desc: ["isNu"],
|
||||
}),
|
||||
]] : [],
|
||||
// verbs that have a perfective prefix that is not و or وا can put the
|
||||
// nu *before* the prefix as well // TODO: also وي prefixes?
|
||||
...((!headSegment.isOoOrWaaHead && !V.isCompound) || (V.voice === "passive")) ? [[
|
||||
makeSegment(nu, ["isNu"]),
|
||||
headSegment.adjust({ ps: removeAccents }),
|
||||
rest.adjust({ ps: removeAccents }),
|
||||
]] : [],
|
||||
];
|
||||
}
|
||||
// // put together all the different possible permutations based on:
|
||||
// // a. potential different versions of where the nu goes
|
||||
// return removeDuplicates(verbWNegativeVersions.flatMap((verbSegments) => {
|
||||
// // for each permutation of the possible ordering of NPs and Verb + nu
|
||||
// // 1. put in kids in the kids section
|
||||
// const segments = oldPutKidsInKidsSection([...blocks, ...verbSegments], kids);
|
||||
// // 2. space out the words properly
|
||||
// const withProperSpaces = addSpacesBetweenSegments(segments);
|
||||
// // 3. throw it all together into a PsString for each permutation
|
||||
// return combineSegments(withProperSpaces);
|
||||
// }));
|
||||
// }
|
||||
|
||||
export function compileEP(EP: T.EPRendered): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string[] };
|
||||
export function compileEP(EP: T.EPRendered, combineLengths: true, blankOut?: BlankoutOptions): { ps: T.PsString[], e?: string[] };
|
||||
|
@ -270,7 +67,36 @@ export function compileEP(EP: T.EPRendered, combineLengths?: boolean, blankOut?:
|
|||
};
|
||||
}
|
||||
|
||||
function compileEPPs(blocks: T.Block[], kids: T.Kid[], omitSubject: boolean, blankOut?: BlankoutOptions): T.SingleOrLengthOpts<T.PsString[]> {
|
||||
export function compileVP(VP: T.VPRendered, form: T.FormVersion): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] };
|
||||
export function compileVP(VP: T.VPRendered, form: T.FormVersion, combineLengths: true): { ps: T.PsString[], e?: string [] };
|
||||
export function compileVP(VP: T.VPRendered, form: T.FormVersion, combineLengths?: true): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] } {
|
||||
const verb = getVerbFromBlocks(VP.blocks).block;
|
||||
const psResult = compileVPPs(VP.blocks, VP.kids, form, VP.king);
|
||||
return {
|
||||
ps: combineLengths ? flattenLengths(psResult) : psResult,
|
||||
// TODO: English doesn't quite work for dynamic compounds in passive voice
|
||||
e: (verb.voice === "passive" && VP.isCompound === "dynamic") ? undefined : compileEnglishVP(VP),
|
||||
};
|
||||
}
|
||||
|
||||
function compileVPPs(blocks: T.Block[][], kids: T.Kid[], form: T.FormVersion, king: "subject" | "object"): T.SingleOrLengthOpts<T.PsString[]> {
|
||||
if (hasVerbWithLengths(blocks)) {
|
||||
return {
|
||||
long: compileVPPs(specifyVerbLength(blocks, "long"), kids, form, king) as T.PsString[],
|
||||
short: compileVPPs(specifyVerbLength(blocks, "short"), kids, form, king) as T.PsString[],
|
||||
};
|
||||
}
|
||||
const subjectPerson = getSubjectSelectionFromBlocks(blocks)
|
||||
.selection.selection.person;
|
||||
const blocksWKids = putKidsInKidsSection(
|
||||
filterForVisibleBlocksVP(blocks, form, king),
|
||||
kids,
|
||||
false,
|
||||
);
|
||||
return removeDuplicates(combineIntoText(blocksWKids, subjectPerson, {}));
|
||||
}
|
||||
|
||||
function compileEPPs(blocks: T.Block[][], kids: T.Kid[], omitSubject: boolean, blankOut?: BlankoutOptions): T.SingleOrLengthOpts<T.PsString[]> {
|
||||
if (hasEquativeWithLengths(blocks)) {
|
||||
return {
|
||||
long: compileEPPs(specifyEquativeLength(blocks, "long"), kids, omitSubject, blankOut) as T.PsString[],
|
||||
|
@ -280,16 +106,51 @@ function compileEPPs(blocks: T.Block[], kids: T.Kid[], omitSubject: boolean, bla
|
|||
const subjectPerson = getSubjectSelectionFromBlocks(blocks)
|
||||
.selection.selection.person;
|
||||
const blocksWKids = putKidsInKidsSection(
|
||||
omitSubject ? blocks.filter(b => b.type !== "subjectSelection") : blocks,
|
||||
omitSubject ? blocks.map(blks => blks.filter(b => b.type !== "subjectSelection")) : blocks,
|
||||
kids,
|
||||
!!blankOut?.kidsSection
|
||||
);
|
||||
// BIG TODO: If the kid's section is blank and there are no kids - add a blank for the kids section!
|
||||
return removeDuplicates(combineIntoText(blocksWKids, subjectPerson, blankOut));
|
||||
}
|
||||
|
||||
function combineIntoText(pieces: (T.Block | T.Kid | T.PsString)[], subjectPerson: T.Person, blankOut?: BlankoutOptions): T.PsString[] {
|
||||
export function filterForVisibleBlocksVP(blocks: T.Block[][], form: T.FormVersion, king: "subject" | "object"): T.Block[][] {
|
||||
const servant = king === "object" ? "subject" : "object";
|
||||
return blocks.map(blks => blks.filter((block) => {
|
||||
if (form.removeKing) {
|
||||
if (
|
||||
(king === "subject" && block.type === "subjectSelection")
|
||||
||
|
||||
(king === "object" && block.type === "objectSelection")
|
||||
) return false;
|
||||
}
|
||||
if (form.shrinkServant) {
|
||||
if (
|
||||
(servant === "subject" && block.type === "subjectSelection")
|
||||
||
|
||||
(servant === "object" && block.type === "objectSelection")
|
||||
) return false;
|
||||
}
|
||||
if (block.type === "objectSelection" && typeof block.selection !== "object") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
export function filterForVisibleBlocksEP(blocks: T.Block[][], omitSubject: boolean): T.Block[][] {
|
||||
if (!omitSubject) return blocks;
|
||||
return blocks.map(blks => blks.filter((block) => {
|
||||
if (block.type === "subjectSelection") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
function combineIntoText(piecesWVars: (T.Block | T.Kid | T.PsString)[][], subjectPerson: T.Person, blankOut?: BlankoutOptions): T.PsString[] {
|
||||
function combine(pieces: (T.Block | T.Kid | T.PsString)[]): T.PsString[] {
|
||||
const first = pieces[0];
|
||||
const next = pieces[1];
|
||||
const rest = pieces.slice(1);
|
||||
const firstPs = ("p" in first)
|
||||
? [first]
|
||||
|
@ -301,11 +162,19 @@ function combineIntoText(pieces: (T.Block | T.Kid | T.PsString)[], subjectPerson
|
|||
if (!rest.length) {
|
||||
return firstPs;
|
||||
}
|
||||
return combineIntoText(rest, subjectPerson, blankOut).flatMap(r => (
|
||||
firstPs.map(fPs => concatPsString(fPs, " ", r))
|
||||
return combine(rest).flatMap(r => (
|
||||
firstPs.map(fPs => concatPsString(
|
||||
fPs,
|
||||
(!("p" in first) && first.type === "perfectiveHead" && !("p" in next) && (next.type === "verb" || next.type === "negative" || next.type === "mini-pronoun"))
|
||||
? ((next.type === "negative" || next.type === "mini-pronoun") ? { p: "", f: "-" } : "")
|
||||
: " ",
|
||||
r,
|
||||
))
|
||||
)
|
||||
);
|
||||
}
|
||||
return piecesWVars.flatMap(combine);
|
||||
}
|
||||
|
||||
function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsString[] {
|
||||
if (piece.type === "ba") {
|
||||
|
@ -314,8 +183,10 @@ function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsSt
|
|||
if (piece.type === "mini-pronoun") {
|
||||
return [piece.ps];
|
||||
}
|
||||
if (piece.type === "nu") {
|
||||
return [{ p: "نه", f: "nú" }];
|
||||
if (piece.type === "negative") {
|
||||
return [
|
||||
negativeParticle[piece.imperative ? "imperative" : "nonImperative"],
|
||||
];
|
||||
}
|
||||
if (piece.type === "equative") {
|
||||
// length will already be specified in compileEPPs - this is just for type safety
|
||||
|
@ -324,12 +195,45 @@ function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsSt
|
|||
if (piece.type === "subjectSelection" || piece.type === "predicateSelection") {
|
||||
return getPashtoFromRendered(piece.selection, subjectPerson);
|
||||
}
|
||||
// if (piece.type === "AP") {
|
||||
if (piece.type === "AP") {
|
||||
return getPashtoFromRendered(piece, subjectPerson);
|
||||
// }
|
||||
}
|
||||
if (piece.type === "perfectiveHead") {
|
||||
return [piece.ps];
|
||||
}
|
||||
if (piece.type === "verbComplement") {
|
||||
return [{ p: "---", f: "---"}]; //getPashtoFromRendered(piece.complement);
|
||||
}
|
||||
if (piece.type === "objectSelection") {
|
||||
if (typeof piece.selection !== "object") {
|
||||
return [{ p: "", f: "" }];
|
||||
}
|
||||
return getPashtoFromRendered(piece.selection, subjectPerson);
|
||||
}
|
||||
if (piece.type === "verb") {
|
||||
// getLong is just for type safety - we will have split up the length options earlier in compileVPPs
|
||||
return getLong(piece.block.ps);
|
||||
}
|
||||
if (piece.type === "perfectParticipleBlock") {
|
||||
// getLong is just for type safety - we will have split up the length options earlier in compileVPPs
|
||||
return getLong(piece.ps);
|
||||
}
|
||||
if (piece.type === "perfectEquativeBlock") {
|
||||
// just using the short one for now - it will only be short anyways
|
||||
return getShort(piece.ps);
|
||||
}
|
||||
if (piece.type === "modalVerbBlock") {
|
||||
// getLong is just for type safety - we will have split up the length options earlier in compileVPPs
|
||||
return getLong(piece.ps);
|
||||
}
|
||||
if (piece.type === "modalVerbKedulPart") {
|
||||
// just using the short one for now - it will only be short anyways
|
||||
return getShort(piece.ps);
|
||||
}
|
||||
throw new Error("unrecognized piece type");
|
||||
}
|
||||
|
||||
function getEngAPs(blocks: T.Block[]): string {
|
||||
function getEngAPs(blocks: T.Block[][]): string {
|
||||
return getAPsFromBlocks(blocks).reduce((accum, curr) => {
|
||||
const e = getEnglishFromRendered(curr);
|
||||
if (!e) return accum;
|
||||
|
@ -337,16 +241,8 @@ function getEngAPs(blocks: T.Block[]): string {
|
|||
}, "");
|
||||
}
|
||||
|
||||
function getEnglishAPs(blocks: (T.Rendered<T.SubjectSelectionComplete> | T.Rendered<T.APSelection> | T.RenderedVPSBlock)[]) {
|
||||
const APs = blocks.filter(x => x.type !== "subjectSelection") as T.Rendered<T.APSelection>[];
|
||||
return APs.reduce((accum, curr) => {
|
||||
const e = getEnglishFromRendered(curr);
|
||||
if (!e) return accum;
|
||||
return `${accum} ${e}`;
|
||||
}, "");
|
||||
}
|
||||
|
||||
function putKidsInKidsSection(blocks: T.Block[], kids: T.Kid[], enforceKidsSectionBlankout: boolean): (T.Block | T.Kid | T.PsString)[] {
|
||||
function putKidsInKidsSection(blocksWVars: T.Block[][], kids: T.Kid[], enforceKidsSectionBlankout: boolean): (T.Block | T.Kid | T.PsString)[][] {
|
||||
function insert(blocks: T.Block[]): (T.Block | T.Kid | T.PsString)[] {
|
||||
const first = blocks[0];
|
||||
const rest = blocks.slice(1);
|
||||
return [
|
||||
|
@ -355,58 +251,21 @@ function putKidsInKidsSection(blocks: T.Block[], kids: T.Kid[], enforceKidsSecti
|
|||
...rest,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
function mergeSegments(s1: Segment, s2: Segment, noSpace?: "no space"): Segment {
|
||||
if (noSpace) {
|
||||
return s2.adjust({ ps: (p) => concatPsString(s1.ps[0], p) });
|
||||
}
|
||||
return s2.adjust({ ps: (p) => concatPsString(s1.ps[0], " ", p) });
|
||||
}
|
||||
|
||||
function addSpacesBetweenSegments(segments: Segment[]): (Segment | " " | "" | T.PsString)[] {
|
||||
const o: (Segment | " " | "" | T.PsString)[] = [];
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
const current = segments[i];
|
||||
const next = segments[i+1];
|
||||
o.push(current);
|
||||
if (!next) break;
|
||||
if (
|
||||
// stative compound part
|
||||
!current.ps[0].p.endsWith(" ")
|
||||
&&
|
||||
(
|
||||
(next.isKidBetweenHeadAndRest || next.isNu)
|
||||
||
|
||||
(next.isVerbRest && current.isKidBetweenHeadAndRest)
|
||||
)
|
||||
) {
|
||||
o.push({
|
||||
f: " ", // TODO: make this "-" in the right places
|
||||
p: ((current.isVerbHead && (next.isMiniPronoun || next.isNu))
|
||||
|| (current.isOoOrWaaHead && (next.isBa || next.isNu))) ? "" : " ", // or if its waa head
|
||||
});
|
||||
} else if (current.isVerbHead && next.isVerbRest) {
|
||||
o.push("");
|
||||
} else {
|
||||
o.push(" ");
|
||||
}
|
||||
}
|
||||
return o;
|
||||
return blocksWVars.map(insert);
|
||||
}
|
||||
|
||||
function compileEnglishVP(VP: T.VPRendered): string[] | undefined {
|
||||
function insertEWords(e: string, { subject, object, APs }: { subject: string, object?: string, APs: string }): string {
|
||||
return e.replace("$SUBJ", subject).replace("$OBJ", object || "") + APs;
|
||||
}
|
||||
const engSubj = getRenderedSubjectSelection(VP.blocks).selection;
|
||||
const obj = getRenderedObjectSelection(VP.blocks).selection;
|
||||
const engSubj = getSubjectSelectionFromBlocks(VP.blocks).selection;
|
||||
const obj = getObjectSelectionFromBlocks(VP.blocks).selection;
|
||||
const engObj = typeof obj === "object"
|
||||
? obj
|
||||
: obj === "none"
|
||||
? ""
|
||||
: undefined;
|
||||
const engAPs = getEnglishAPs(VP.blocks);
|
||||
const engAPs = getEngAPs(VP.blocks);
|
||||
// require all English parts for making the English phrase
|
||||
return (VP.englishBase && engSubj && engObj !== undefined)
|
||||
? VP.englishBase.map(e => insertEWords(e, {
|
||||
|
@ -438,27 +297,7 @@ function compileEnglishEP(EP: T.EPRendered): string[] | undefined {
|
|||
return b;
|
||||
}
|
||||
|
||||
export function orderKidsSection(kids: Segment[]): Segment[] {
|
||||
const sorted = [...kids];
|
||||
return sorted.sort((a, b) => {
|
||||
// ba first
|
||||
if (a.isBa) return -1;
|
||||
// kinds lined up 1st 2nd 3rd person
|
||||
if (a.isMiniPronoun && b.isMiniPronoun) {
|
||||
if (a.isMiniPronoun < b.isMiniPronoun) {
|
||||
return -1;
|
||||
}
|
||||
if (a.isMiniPronoun > b.isMiniPronoun) {
|
||||
return 1;
|
||||
}
|
||||
// TODO: is this enough?
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
export function checkEPForMiniPronounsError(s: T.EPSelectionState): undefined | string {
|
||||
export function checkForMiniPronounsError(s: T.EPSelectionState | T.VPSelectionState): undefined | string {
|
||||
function findDuplicateMiniP(mp: T.MiniPronoun[]): T.MiniPronoun | undefined {
|
||||
const duplicates = mp.filter((item, index) => (
|
||||
mp.findIndex(m => item.ps.p === m.ps.p) !== index
|
||||
|
@ -467,10 +306,14 @@ export function checkEPForMiniPronounsError(s: T.EPSelectionState): undefined |
|
|||
return duplicates[0];
|
||||
}
|
||||
const kids = (() => {
|
||||
if ("predicate" in s) {
|
||||
const EPS = completeEPSelection(s);
|
||||
if (!EPS) return undefined;
|
||||
const { kids } = renderEP(EPS);
|
||||
return kids;
|
||||
return renderEP(EPS).kids;
|
||||
};
|
||||
const VPS = completeVPSelection(s);
|
||||
if (!VPS) return undefined;
|
||||
return renderVP(VPS).kids;
|
||||
})();
|
||||
if (!kids) return undefined;
|
||||
const miniPronouns = kids.filter(x => x.type === "mini-pronoun") as T.MiniPronoun[];
|
||||
|
@ -484,67 +327,6 @@ export function checkEPForMiniPronounsError(s: T.EPSelectionState): undefined |
|
|||
return undefined;
|
||||
}
|
||||
|
||||
export function checkForMiniPronounsError(s: T.VPSelectionState): undefined | string {
|
||||
function findDuplicateMiniPronoun(mp: Segment[]): Segment | undefined {
|
||||
const duplicates = mp.filter((item, index) => (
|
||||
mp.findIndex(m => item.ps[0].p === m.ps[0].p) !== index
|
||||
));
|
||||
if (duplicates.length === 0) return undefined;
|
||||
return duplicates[0];
|
||||
}
|
||||
const kids = (() => {
|
||||
const VPS = completeVPSelection(s);
|
||||
if (!VPS) return undefined;
|
||||
const { kids } = getVPSegmentsAndKids(renderVP(VPS));
|
||||
return kids;
|
||||
})();
|
||||
if (!kids) return undefined;
|
||||
const miniPronouns = kids.filter(x => x.isMiniPronoun);
|
||||
if (miniPronouns.length > 2) {
|
||||
return "can't add another mini-pronoun, there are alread two";
|
||||
}
|
||||
const duplicateMiniPronoun = findDuplicateMiniPronoun(miniPronouns);
|
||||
if (duplicateMiniPronoun) {
|
||||
return `there's already a ${duplicateMiniPronoun.ps[0].p} - ${duplicateMiniPronoun.ps[0].f} mini-pronoun in use, can't have two of those`;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function findPossesivesToShrinkInVP(VP: T.VPRendered, f: {
|
||||
shrunkServant: boolean,
|
||||
removedKing: boolean,
|
||||
}): T.Rendered<T.NPSelection>[] {
|
||||
return VP.blocks.reduce((found, block) => {
|
||||
if (block.type === "subjectSelection") {
|
||||
if (block.selection.selection.role === "king" && f.removedKing) return found;
|
||||
if (block.selection.selection.role === "servant" && f.shrunkServant) return found;
|
||||
return [
|
||||
...findPossesivesInNP(block.selection),
|
||||
...found,
|
||||
];
|
||||
}
|
||||
if (block.type === "objectSelection") {
|
||||
if (typeof block.selection !== "object") return found;
|
||||
if (block.selection.selection.role === "king" && f.removedKing) return found;
|
||||
if (block.selection.selection.role === "servant" && f.shrunkServant) return found;
|
||||
return [
|
||||
...findPossesivesInNP(block.selection),
|
||||
...found,
|
||||
];
|
||||
}
|
||||
if (block.selection.type === "sandwich") {
|
||||
if (block.selection.inside.selection.type === "pronoun") {
|
||||
return found;
|
||||
}
|
||||
return [
|
||||
...findPossesivesInNP(block.selection.inside),
|
||||
...found,
|
||||
];
|
||||
}
|
||||
return found;
|
||||
}, [] as T.Rendered<T.NPSelection>[]);
|
||||
}
|
||||
|
||||
function findPossesivesInNP(NP: T.Rendered<T.NPSelection> | T.ObjectNP | undefined): T.Rendered<T.NPSelection>[] {
|
||||
if (NP === undefined) return [];
|
||||
if (typeof NP !== "object") return [];
|
||||
|
@ -574,24 +356,13 @@ function findPossesivesInAdjective(a: T.Rendered<T.AdjectiveSelection>): T.Rende
|
|||
return findPossesivesInNP(a.sandwich.inside);
|
||||
}
|
||||
|
||||
|
||||
// export function findPossesiveToShrinkInVP(VP: T.VPRendered): T.Rendered<T.NPSelection>[] {
|
||||
// const obj: T.Rendered<T.NPSelection> | undefined = ("object" in VP && typeof VP.object === "object")
|
||||
// ? VP.object
|
||||
// : undefined;
|
||||
// return [
|
||||
// ...findPossesivesInNP(VP.subject),
|
||||
// ...findPossesivesInNP(obj),
|
||||
// ];
|
||||
// }
|
||||
|
||||
export function shrinkNP(np: T.Rendered<T.NPSelection>): Segment {
|
||||
function getFirstSecThird(): 1 | 2 | 3 {
|
||||
if ([0, 1, 6, 7].includes(np.selection.person)) return 1;
|
||||
if ([2, 3, 8, 9].includes(np.selection.person)) return 2;
|
||||
return 3;
|
||||
export function flattenLengths(r: T.SingleOrLengthOpts<T.PsString[] | T.PsString>): T.PsString[] {
|
||||
if ("long" in r) {
|
||||
return Object.values(r).flat();
|
||||
}
|
||||
const [row, col] = getVerbBlockPosFromPerson(np.selection.person);
|
||||
return makeSegment(pronouns.mini[row][col], ["isKid", getFirstSecThird()]);
|
||||
if (Array.isArray(r)) {
|
||||
return r;
|
||||
}
|
||||
return [r];
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
import * as T from "../../types";
|
||||
import {
|
||||
getPersonFromNP,
|
||||
} from "./vp-tools";
|
||||
import { pronouns } from "../grammar-units";
|
||||
import { getVerbBlockPosFromPerson } from "../misc-helpers";
|
||||
import { getFirstSecThird } from "../../lib/misc-helpers";
|
||||
|
||||
export function findPossesivesToShrink(
|
||||
blocks: (T.EPSBlockComplete | T.VPSBlockComplete | T.SubjectSelectionComplete | T.PredicateSelectionComplete | T.APSelection)[],
|
||||
): T.MiniPronoun[] {
|
||||
return blocks.reduce((kids, item) => {
|
||||
const block = "block" in item ? item.block : item;
|
||||
if (block.type === "subjectSelection") {
|
||||
return [
|
||||
...kids,
|
||||
...findShrunkenPossInNP(block.selection),
|
||||
];
|
||||
}
|
||||
if (block.type === "objectSelection") {
|
||||
if (typeof block.selection !== "object") return kids;
|
||||
return [
|
||||
...kids,
|
||||
...findShrunkenPossInNP(block.selection),
|
||||
];
|
||||
}
|
||||
if (block.type === "AP") {
|
||||
if (block.selection.type === "adverb") return kids;
|
||||
return [
|
||||
...kids,
|
||||
...findShrunkenPossInNP(block.selection.inside),
|
||||
];
|
||||
}
|
||||
if (block.type === "predicateSelection") {
|
||||
if (block.selection.type === "EQComp") {
|
||||
if (block.selection.selection.type === "sandwich") {
|
||||
return [
|
||||
...kids,
|
||||
...findShrunkenPossInNP(block.selection.selection.inside),
|
||||
];
|
||||
}
|
||||
return kids;
|
||||
}
|
||||
return [
|
||||
...kids,
|
||||
...findShrunkenPossInNP(block.selection),
|
||||
];
|
||||
}
|
||||
return kids;
|
||||
}, [] as T.MiniPronoun[]);
|
||||
}
|
||||
|
||||
function findShrunkenPossInNP(NP: T.NPSelection): T.MiniPronoun[] {
|
||||
if (NP.selection.type === "pronoun") return [];
|
||||
if (!NP.selection.possesor) return [];
|
||||
// if (NP.selection.type === "noun") {
|
||||
// if (NP.selection.adjectives) {
|
||||
// const { adjectives, ...rest } = NP.selection;
|
||||
// return [
|
||||
// // TODO: ability to find possesives shrinkage in sandwiches in adjectives
|
||||
// // ...findShrunkenPossInAdjectives(adjectives),
|
||||
// ...findShrunkenPossInNP({ type: "NP", selection: {
|
||||
// ...rest,
|
||||
// adjectives: [],
|
||||
// }}),
|
||||
// ];
|
||||
// }
|
||||
// }
|
||||
if (NP.selection.possesor.shrunken) {
|
||||
const person = getPersonFromNP(NP.selection.possesor.np);
|
||||
const miniP: T.MiniPronoun = {
|
||||
type: "mini-pronoun",
|
||||
person,
|
||||
ps: getMiniPronounPs(person),
|
||||
source: "possesive",
|
||||
np: NP.selection.possesor.np,
|
||||
};
|
||||
return [miniP];
|
||||
}
|
||||
return findShrunkenPossInNP(NP.selection.possesor.np);
|
||||
}
|
||||
|
||||
export function getMiniPronounPs(person: T.Person): T.PsString {
|
||||
const [row, col] = getVerbBlockPosFromPerson(person);
|
||||
return pronouns.mini[row][col][0];
|
||||
}
|
||||
|
||||
export function orderKids(kids: T.Kid[]): T.Kid[] {
|
||||
const sorted = [...kids].sort((a, b) => {
|
||||
// ba first
|
||||
if (a.type === "ba") return -1;
|
||||
// kinds lined up 1st 2nd 3rd person
|
||||
if (a.type === "mini-pronoun" && b.type === "mini-pronoun") {
|
||||
const aPers = getFirstSecThird(a.person);
|
||||
const bPers = getFirstSecThird(b.person);
|
||||
if (aPers < bPers) {
|
||||
return -1;
|
||||
}
|
||||
if (aPers > bPers) {
|
||||
return 1;
|
||||
}
|
||||
// TODO: is this enough?
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
return sorted;
|
||||
}
|
|
@ -4,7 +4,7 @@ import {
|
|||
getPersonFromNP,
|
||||
} from "./vp-tools";
|
||||
import { renderNPSelection } from "./render-np";
|
||||
import { getFirstSecThird, getPersonFromVerbForm } from "../../lib/misc-helpers";
|
||||
import { getPersonFromVerbForm } from "../../lib/misc-helpers";
|
||||
import { getVerbBlockPosFromPerson } from "../misc-helpers";
|
||||
import { getEnglishWord } from "../get-english-word";
|
||||
import { psStringFromEntry } from "../p-text-helpers";
|
||||
|
@ -13,7 +13,7 @@ import { renderAdjectiveSelection } from "./render-adj";
|
|||
import { renderSandwich } from "./render-sandwich";
|
||||
import { EPSBlocksAreComplete, getSubjectSelection } from "./blocks-utils";
|
||||
import { removeAccentsWLength } from "../accent-helpers";
|
||||
import { pronouns } from "../grammar-units";
|
||||
import { findPossesivesToShrink, orderKids } from "./render-common";
|
||||
|
||||
export function renderEP(EP: T.EPSelectionComplete): T.EPRendered {
|
||||
const { kids, blocks, englishEquativePerson } = getEPSBlocksAndKids(EP);
|
||||
|
@ -29,7 +29,7 @@ export function renderEP(EP: T.EPSelectionComplete): T.EPRendered {
|
|||
};
|
||||
}
|
||||
|
||||
function getEPSBlocksAndKids(EP: T.EPSelectionComplete): { kids: T.Kid[], blocks: T.Block[], englishEquativePerson: T.Person } {
|
||||
function getEPSBlocksAndKids(EP: T.EPSelectionComplete): { kids: T.Kid[], blocks: T.Block[][], englishEquativePerson: T.Person } {
|
||||
const subject = getSubjectSelection(EP.blocks).selection;
|
||||
const subjectPerson = getPersonFromNP(subject);
|
||||
const commandingNP: T.NPSelection = subject.selection.type === "pronoun"
|
||||
|
@ -39,7 +39,7 @@ function getEPSBlocksAndKids(EP: T.EPSelectionComplete): { kids: T.Kid[], blocks
|
|||
: subject;
|
||||
const commandingPerson = getPersonFromNP(commandingNP);
|
||||
const equative: T.EquativeBlock = { type: "equative", equative: renderEquative(EP.equative, commandingPerson) };
|
||||
const blocks: T.Block[] = [
|
||||
const blocks: T.Block[][] = insertNegative([
|
||||
...renderEPSBlocks(EP.blocks),
|
||||
{
|
||||
type: "predicateSelection",
|
||||
|
@ -47,10 +47,9 @@ function getEPSBlocksAndKids(EP: T.EPSelectionComplete): { kids: T.Kid[], blocks
|
|||
? renderNPSelection(EP.predicate.selection, false, false, "subject", "king")
|
||||
: renderEqCompSelection(EP.predicate.selection, commandingPerson),
|
||||
},
|
||||
...EP.equative.negative ? [{ type: "nu" } as T.Block] : [],
|
||||
EP.equative.negative ? removeAccontsFromEq(equative) : equative,
|
||||
];
|
||||
const miniPronouns = findPossesivesToShrink([...EP.blocks, EP.predicate], EP.omitSubject);
|
||||
equative,
|
||||
], EP.equative.negative);
|
||||
const miniPronouns = findPossesivesToShrink(removeOrKeepSubject([...EP.blocks, EP.predicate], EP.omitSubject));
|
||||
const kids: T.Kid[] = orderKids([
|
||||
...equative.equative.hasBa ? [{ type: "ba" } as T.Kid] : [],
|
||||
...miniPronouns,
|
||||
|
@ -64,92 +63,27 @@ function getEPSBlocksAndKids(EP: T.EPSelectionComplete): { kids: T.Kid[], blocks
|
|||
};
|
||||
}
|
||||
|
||||
function orderKids(kids: T.Kid[]): T.Kid[] {
|
||||
const sorted = [...kids].sort((a, b) => {
|
||||
// ba first
|
||||
if (a.type === "ba") return -1;
|
||||
// kinds lined up 1st 2nd 3rd person
|
||||
if (a.type === "mini-pronoun" && b.type === "mini-pronoun") {
|
||||
const aPers = getFirstSecThird(a.person);
|
||||
const bPers = getFirstSecThird(b.person);
|
||||
if (aPers < bPers) {
|
||||
return -1;
|
||||
}
|
||||
if (aPers > bPers) {
|
||||
return 1;
|
||||
}
|
||||
// TODO: is this enough?
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
return sorted;
|
||||
function insertNegative(blocks: T.Block[], negative: boolean): T.Block[][] {
|
||||
if (!negative) return [blocks];
|
||||
const blocksA = removeAccentsFromEq(blocks);
|
||||
return [
|
||||
[
|
||||
...blocksA.slice(0, blocks.length - 1),
|
||||
{ type: "negative", imperative: false },
|
||||
...blocksA.slice(-1), // last (equative)
|
||||
],
|
||||
[
|
||||
...blocksA.slice(0, blocks.length - 2),
|
||||
{ type: "negative", imperative: false },
|
||||
...blocksA.slice(-1), // last (equative)
|
||||
...blocksA.slice(-2, -1), // second last (predicate)
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
function findPossesivesToShrink(blocks: (T.EPSBlockComplete | T.SubjectSelectionComplete | T.PredicateSelectionComplete | T.APSelection)[], omitSubject: boolean): T.MiniPronoun[] {
|
||||
return blocks.reduce((kids, item) => {
|
||||
const block = "block" in item ? item.block : item;
|
||||
if (block.type === "subjectSelection") {
|
||||
if (omitSubject) return kids;
|
||||
return [
|
||||
...kids,
|
||||
...findShrunkenPossInNP(block.selection),
|
||||
];
|
||||
}
|
||||
if (block.type === "AP") {
|
||||
if (block.selection.type === "adverb") return kids;
|
||||
return [
|
||||
...kids,
|
||||
...findShrunkenPossInNP(block.selection.inside),
|
||||
];
|
||||
}
|
||||
if (block.type === "predicateSelection") {
|
||||
if (block.selection.type === "EQComp") {
|
||||
if (block.selection.selection.type === "sandwich") {
|
||||
return [
|
||||
...kids,
|
||||
...findShrunkenPossInNP(block.selection.selection.inside),
|
||||
];
|
||||
}
|
||||
return kids;
|
||||
}
|
||||
return [
|
||||
...kids,
|
||||
...findShrunkenPossInNP(block.selection),
|
||||
];
|
||||
}
|
||||
return kids;
|
||||
}, [] as T.MiniPronoun[]);
|
||||
}
|
||||
|
||||
function findShrunkenPossInNP(NP: T.NPSelection): T.MiniPronoun[] {
|
||||
if (NP.selection.type === "pronoun") return [];
|
||||
if (!NP.selection.possesor) return [];
|
||||
// if (NP.selection.type === "noun") {
|
||||
// if (NP.selection.adjectives) {
|
||||
// const { adjectives, ...rest } = NP.selection;
|
||||
// return [
|
||||
// // TODO: ability to find possesives shrinkage in sandwiches in adjectives
|
||||
// // ...findShrunkenPossInAdjectives(adjectives),
|
||||
// ...findShrunkenPossInNP({ type: "NP", selection: {
|
||||
// ...rest,
|
||||
// adjectives: [],
|
||||
// }}),
|
||||
// ];
|
||||
// }
|
||||
// }
|
||||
if (NP.selection.possesor.shrunken) {
|
||||
const person = getPersonFromNP(NP.selection.possesor.np);
|
||||
const miniP: T.MiniPronoun = {
|
||||
type: "mini-pronoun",
|
||||
person,
|
||||
ps: getMiniPronounPs(person),
|
||||
source: "possesive",
|
||||
np: NP.selection.possesor.np,
|
||||
};
|
||||
return [miniP];
|
||||
}
|
||||
return findShrunkenPossInNP(NP.selection.possesor.np);
|
||||
function removeOrKeepSubject(blocks: (T.EPSBlockComplete | T.SubjectSelectionComplete | T.PredicateSelectionComplete | T.APSelection)[], omitSubject: boolean): (T.EPSBlockComplete | T.SubjectSelectionComplete | T.PredicateSelectionComplete | T.APSelection)[] {
|
||||
if (!omitSubject) return blocks;
|
||||
return blocks.filter(b => !("type" in b && b.type === "subjectSelection"));
|
||||
}
|
||||
|
||||
export function getEquativeForm(tense: T.EquativeTense): { hasBa: boolean, form: T.SingleOrLengthOpts<T.VerbBlock> } {
|
||||
|
@ -343,17 +277,18 @@ export function completeEPSelection(eps: T.EPSelectionState): T.EPSelectionCompl
|
|||
};
|
||||
}
|
||||
|
||||
export function getMiniPronounPs(person: T.Person): T.PsString {
|
||||
const [row, col] = getVerbBlockPosFromPerson(person);
|
||||
return pronouns.mini[row][col][0];
|
||||
}
|
||||
|
||||
function removeAccontsFromEq(equ: T.EquativeBlock): T.EquativeBlock {
|
||||
return {
|
||||
...equ,
|
||||
function removeAccentsFromEq(blocks: T.Block[]): T.Block[] {
|
||||
return blocks.map((block) => {
|
||||
if (block.type === "equative") {
|
||||
const e: T.EquativeBlock = {
|
||||
...block,
|
||||
equative: {
|
||||
...equ.equative,
|
||||
ps: removeAccentsWLength(equ.equative.ps),
|
||||
...block.equative,
|
||||
ps: removeAccentsWLength(block.equative.ps),
|
||||
},
|
||||
};
|
||||
return e;
|
||||
}
|
||||
return block;
|
||||
});
|
||||
}
|
|
@ -17,8 +17,8 @@ import { renderAdjectiveSelection } from "./render-adj";
|
|||
import { isPattern5Entry, isAnimNounEntry } from "../type-predicates";
|
||||
|
||||
export function renderNPSelection(NP: T.NPSelection, inflected: boolean, inflectEnglish: boolean, role: "subject", soRole: "servant" | "king" | "none"): T.Rendered<T.NPSelection>;
|
||||
export function renderNPSelection(NP: T.NPSelection | T.ObjectNP, inflected: boolean, inflectEnglish: boolean, role: "object", soRole: "servant" | "king" | "none"): T.Rendered<T.NPSelection> | T.Person.ThirdPlurMale | "none";
|
||||
export function renderNPSelection(NP: T.NPSelection | T.ObjectNP, inflected: boolean, inflectEnglish: boolean, role: "subject" | "object", soRole: "servant" | "king" | "none"): T.Rendered<T.NPSelection> | T.Person.ThirdPlurMale | "none" {
|
||||
export function renderNPSelection(NP: T.NPSelection, inflected: boolean, inflectEnglish: boolean, role: "object", soRole: "servant" | "king" | "none"): T.Rendered<T.NPSelection>;
|
||||
export function renderNPSelection(NP: T.NPSelection, inflected: boolean, inflectEnglish: boolean, role: "subject" | "object", soRole: "servant" | "king" | "none"): T.Rendered<T.NPSelection> {
|
||||
if (typeof NP !== "object") {
|
||||
if (role !== "object") {
|
||||
throw new Error("ObjectNP only allowed for objects");
|
||||
|
|
|
@ -7,8 +7,10 @@ import {
|
|||
hasBaParticle,
|
||||
getLong,
|
||||
isImperativeBlock,
|
||||
splitOffLeapfrogWordFull,
|
||||
getShort,
|
||||
} from "../p-text-helpers";
|
||||
import { removeAccents } from "../accent-helpers";
|
||||
import { removeAccents, removeAccentsWLength } from "../accent-helpers";
|
||||
import {
|
||||
getPersonFromNP,
|
||||
removeBa,
|
||||
|
@ -16,6 +18,8 @@ import {
|
|||
getTenseVerbForm,
|
||||
} from "./vp-tools";
|
||||
import {
|
||||
isImperativeTense,
|
||||
isModalTense,
|
||||
isPattern4Entry,
|
||||
isPerfectTense,
|
||||
} from "../type-predicates";
|
||||
|
@ -24,6 +28,7 @@ import { personGender } from "../../lib/misc-helpers";
|
|||
import { renderNPSelection } from "./render-np";
|
||||
import { getObjectSelection, getSubjectSelection } from "./blocks-utils";
|
||||
import { renderAPSelection } from "./render-ap";
|
||||
import { findPossesivesToShrink, orderKids, getMiniPronounPs } from "./render-common";
|
||||
|
||||
// TODO: ISSUE GETTING SPLIT HEAD NOT MATCHING WITH FUTURE VERBS
|
||||
|
||||
|
@ -47,6 +52,12 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
|
|||
const inflectSubject = isPast && isTransitive && !isMascSingAnimatePattern4(subject);
|
||||
const inflectObject = !isPast && isFirstOrSecondPersPronoun(object);
|
||||
// Render Elements
|
||||
const firstBlocks = renderVPBlocks(VP.blocks, {
|
||||
inflectSubject,
|
||||
inflectObject,
|
||||
king,
|
||||
});
|
||||
const { verbBlocks, hasBa } = renderVerbSelection(VP.verb, kingPerson, objectPerson);
|
||||
const b: T.VPRendered = {
|
||||
type: "VPRendered",
|
||||
king,
|
||||
|
@ -54,12 +65,11 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
|
|||
isPast,
|
||||
isTransitive,
|
||||
isCompound: VP.verb.isCompound,
|
||||
blocks: renderVPBlocks(VP.blocks, {
|
||||
inflectSubject,
|
||||
inflectObject,
|
||||
king,
|
||||
}),
|
||||
verb: renderVerbSelection(VP.verb, kingPerson, objectPerson),
|
||||
blocks: insertNegative([
|
||||
...firstBlocks,
|
||||
...verbBlocks,
|
||||
], VP.verb.negative, isImperativeTense(VP.verb.tense)),
|
||||
kids: getVPKids(hasBa, VP.blocks, VP.form, king),
|
||||
englishBase: renderEnglishVPBase({
|
||||
subjectPerson,
|
||||
object: VP.verb.isCompound === "dynamic" ? "none" : object,
|
||||
|
@ -71,28 +81,271 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
|
|||
return b;
|
||||
}
|
||||
|
||||
// function arrangeVerbWNegative(head: T.PsString | undefined, restRaw: T.PsString[], V: T.VerbRendered): Segment[][] {
|
||||
// const hasLeapfrog = isPerfectTense(V.tense) || isModalTense(V.tense);
|
||||
// const rest = (() => {
|
||||
// if (hasLeapfrog) {
|
||||
// const [restF, restLast] = splitOffLeapfrogWord(restRaw);
|
||||
// return {
|
||||
// front: makeSegment(restF.map(removeBa), ["isVerbRest"]),
|
||||
// last: makeSegment(restLast.map(removeBa), ["isVerbRest"]),
|
||||
// };
|
||||
// }
|
||||
// return makeSegment(restRaw.map(removeBa), ["isVerbRest"]);
|
||||
// })();
|
||||
// const headSegment: Segment | undefined = !head
|
||||
// ? head
|
||||
// : makeSegment(
|
||||
// head,
|
||||
// (head.p === "و" || head.p === "وا")
|
||||
// ? ["isVerbHead", "isOoOrWaaHead"]
|
||||
// : ["isVerbHead"]
|
||||
// );
|
||||
// if (!V.negative) {
|
||||
// if ("front" in rest) {
|
||||
// return [
|
||||
// headSegment ? [headSegment, rest.front, rest.last] : [rest.front, rest.last],
|
||||
// ]
|
||||
// }
|
||||
// return [
|
||||
// headSegment ? [headSegment, rest] : [rest],
|
||||
// ];
|
||||
// }
|
||||
// const nu: T.PsString = isImperativeTense(V.tense)
|
||||
// ? { p: "مه", f: "mú" }
|
||||
// : { p: "نه", f: "nú" };
|
||||
// if (!headSegment) {
|
||||
// if ("front" in rest) {
|
||||
// return [
|
||||
// // pefect nu dey me leeduley and nu me dey leeduley
|
||||
// // actually don't think this is correct - keeping it out for now
|
||||
// // [
|
||||
// // mergeSegments(
|
||||
// // makeSegment(nu, ["isNu"]),
|
||||
// // rest.last.adjust({ ps: removeAccents }),
|
||||
// // ),
|
||||
// // rest.front.adjust({ ps: removeAccents }),
|
||||
// // ],
|
||||
// [
|
||||
// makeSegment(nu, ["isNu"]),
|
||||
// rest.last.adjust({ ps: removeAccents }),
|
||||
// rest.front.adjust({ ps: removeAccents }),
|
||||
// ],
|
||||
// [
|
||||
// rest.front.adjust({ ps: removeAccents }),
|
||||
// makeSegment(nu, ["isNu"]),
|
||||
// rest.last.adjust({ ps: removeAccents }),
|
||||
// ],
|
||||
// ];
|
||||
// }
|
||||
// return [[
|
||||
// makeSegment(nu, ["isNu"]),
|
||||
// rest.adjust({ ps: removeAccents }),
|
||||
// ]];
|
||||
// }
|
||||
// if ("front" in rest) {
|
||||
// return [
|
||||
// [
|
||||
// headSegment.adjust({ ps: removeAccents }),
|
||||
// rest.last.adjust({
|
||||
// ps: r => concatPsString(nu, " ", removeAccents(r)),
|
||||
// desc: ["isNu"],
|
||||
// }),
|
||||
// rest.front.adjust({
|
||||
// ps: r => removeAccents(r),
|
||||
// }),
|
||||
// ],
|
||||
// [
|
||||
// headSegment.adjust({ ps: removeAccents }),
|
||||
// rest.front.adjust({
|
||||
// ps: r => concatPsString(nu, " ", removeAccents(r)),
|
||||
// desc: ["isNu"],
|
||||
// }),
|
||||
// rest.last.adjust({
|
||||
// ps: r => removeAccents(r),
|
||||
// }),
|
||||
// ],
|
||||
// ...(!headSegment.isOoOrWaaHead && !V.isCompound) ? [[
|
||||
// mergeSegments(headSegment, rest.front, "no space").adjust({
|
||||
// ps: r => concatPsString(nu, " ", removeAccents(r)),
|
||||
// desc: ["isNu"],
|
||||
// }),
|
||||
// rest.last.adjust({
|
||||
// ps: r => removeAccents(r),
|
||||
// }),
|
||||
// ]] : [],
|
||||
// ];
|
||||
// }
|
||||
// return [
|
||||
// ...(V.voice !== "passive") ? [[
|
||||
// ...headSegment ? [headSegment.adjust({ ps: removeAccents })] : [],
|
||||
// rest.adjust({
|
||||
// ps: r => concatPsString(nu, " ", removeAccents(r)),
|
||||
// desc: ["isNu"],
|
||||
// }),
|
||||
// ]] : [],
|
||||
// // verbs that have a perfective prefix that is not و or وا can put the
|
||||
// // nu *before* the prefix as well // TODO: also وي prefixes?
|
||||
// ...((!headSegment.isOoOrWaaHead && !V.isCompound) || (V.voice === "passive")) ? [[
|
||||
// makeSegment(nu, ["isNu"]),
|
||||
// headSegment.adjust({ ps: removeAccents }),
|
||||
// rest.adjust({ ps: removeAccents }),
|
||||
// ]] : [],
|
||||
// ];
|
||||
// }
|
||||
|
||||
function getVPKids(hasBa: boolean, blocks: T.VPSBlockComplete[], form: T.FormVersion, king: "subject" | "object"): T.Kid[] {
|
||||
const subject = getSubjectSelection(blocks).selection;
|
||||
const objectS = getObjectSelection(blocks).selection;
|
||||
const object = typeof objectS === "object" ? objectS : undefined;
|
||||
const servantNP = king === "subject" ? object : subject;
|
||||
const shrunkenServant = (form.shrinkServant && servantNP)
|
||||
? shrinkServant(servantNP)
|
||||
: undefined;
|
||||
const shrunkenPossesives = findPossesivesToShrink(removeAbbreviated(blocks, form, king));
|
||||
return orderKids([
|
||||
...hasBa ? [{ type: "ba" } as T.Kid] : [],
|
||||
...shrunkenServant ? [shrunkenServant] : [],
|
||||
...shrunkenPossesives ? shrunkenPossesives : [],
|
||||
]);
|
||||
}
|
||||
|
||||
function removeAbbreviated(blocks: T.VPSBlockComplete[], form: T.FormVersion, king: "subject" | "object"): T.VPSBlockComplete[] {
|
||||
return blocks.filter(({ block }) => {
|
||||
if (block.type === "subjectSelection") {
|
||||
if (form.shrinkServant && king === "object") return false;
|
||||
if (form.removeKing && king === "subject") return false;
|
||||
}
|
||||
if (block.type === "objectSelection") {
|
||||
if (form.shrinkServant && king === "subject") return false;
|
||||
if (form.removeKing && king === "object") return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
}
|
||||
|
||||
function insertNegative(blocks: T.Block[], negative: boolean, imperative: boolean): T.Block[][] {
|
||||
// TODO: proper arrange verb with negative and variations
|
||||
// ALSO removing the accent from nu
|
||||
if (!negative) return [blocks];
|
||||
const blocksA = removeVerbAccent(blocks);
|
||||
const basic: T.Block[] = [
|
||||
...blocksA.slice(0, blocks.length - 1),
|
||||
{
|
||||
type: "negative",
|
||||
imperative,
|
||||
},
|
||||
...blocksA.slice(-1),
|
||||
];
|
||||
if (hasNonStandardPerfectiveSplit(blocks)) {
|
||||
return [
|
||||
basic,
|
||||
[
|
||||
...blocksA.slice(0, blocks.length - 2),
|
||||
{ type: "negative", imperative },
|
||||
...blocksA.slice(-2, -1), // second last (perfective split)
|
||||
...blocksA.slice(-1), // last (verb)
|
||||
],
|
||||
];
|
||||
}
|
||||
if (hasLeapFroggable(blocks)) {
|
||||
return [
|
||||
[
|
||||
...blocksA.slice(0, blocks.length - 2),
|
||||
{ type: "negative", imperative },
|
||||
...blocksA.slice(-1), // last
|
||||
...blocksA.slice(-2, -1), // second last
|
||||
],
|
||||
basic,
|
||||
];
|
||||
}
|
||||
return [basic];
|
||||
}
|
||||
|
||||
function hasLeapFroggable(blocks: T.Block[]): boolean {
|
||||
return blocks.some(b => b.type === "perfectEquativeBlock" || b.type === "modalVerbBlock");
|
||||
}
|
||||
|
||||
function hasNonStandardPerfectiveSplit(blocks: T.Block[]): boolean {
|
||||
const perfS = blocks.find(b => b.type === "perfectiveHead");
|
||||
if (!perfS || perfS.type !== "perfectiveHead") {
|
||||
return false;
|
||||
}
|
||||
return !["و", "وا"].includes(perfS.ps.p);
|
||||
}
|
||||
|
||||
function removeVerbAccent(blocks: T.Block[]): T.Block[] {
|
||||
return blocks.map((block) => {
|
||||
if (block.type === "perfectiveHead") {
|
||||
return {
|
||||
...block,
|
||||
ps: removeAccents(block.ps),
|
||||
};
|
||||
}
|
||||
if (block.type === "verb") {
|
||||
return {
|
||||
...block,
|
||||
block: {
|
||||
...block.block,
|
||||
ps: removeAccentsWLength(block.block.ps),
|
||||
},
|
||||
};
|
||||
}
|
||||
return block;
|
||||
});
|
||||
}
|
||||
|
||||
function shrinkServant(np: T.NPSelection): T.MiniPronoun {
|
||||
const person = getPersonFromNP(np);
|
||||
return {
|
||||
type: "mini-pronoun",
|
||||
person,
|
||||
ps: getMiniPronounPs(person),
|
||||
source: "servant",
|
||||
np,
|
||||
};
|
||||
}
|
||||
|
||||
function renderVPBlocks(blocks: T.VPSBlockComplete[], config: {
|
||||
inflectSubject: boolean,
|
||||
inflectObject: boolean,
|
||||
king: "subject" | "object",
|
||||
}): T.RenderedVPSBlock[] {
|
||||
return blocks.map(({ block }): T.RenderedVPSBlock => {
|
||||
}): T.Block[] {
|
||||
return blocks.reduce((blocks, { block }): T.Block[] => {
|
||||
if (block.type === "subjectSelection") {
|
||||
return {
|
||||
return [
|
||||
...blocks,
|
||||
{
|
||||
type: "subjectSelection",
|
||||
selection: renderNPSelection(block.selection, config.inflectSubject, false, "subject", config.king === "subject" ? "king" : "servant"),
|
||||
}
|
||||
},
|
||||
];
|
||||
}
|
||||
if (block.type === "objectSelection") {
|
||||
const object = typeof block === "object" ? block.selection : block;
|
||||
if (typeof object !== "object") {
|
||||
return [
|
||||
...blocks,
|
||||
{
|
||||
type: "objectSelection",
|
||||
selection: object,
|
||||
},
|
||||
];
|
||||
}
|
||||
const selection = renderNPSelection(object, config.inflectObject, true, "object", config.king === "object" ? "king" : "servant");
|
||||
return {
|
||||
return [
|
||||
...blocks,
|
||||
{
|
||||
type: "objectSelection",
|
||||
selection,
|
||||
};
|
||||
},
|
||||
];
|
||||
}
|
||||
return renderAPSelection(block);
|
||||
});
|
||||
return [
|
||||
...blocks,
|
||||
renderAPSelection(block),
|
||||
];
|
||||
}, [] as T.Block[]);
|
||||
}
|
||||
|
||||
function whatsAdjustable(VP: T.VPSelectionComplete): "both" | "king" | "servant" {
|
||||
|
@ -109,7 +362,15 @@ function whatsAdjustable(VP: T.VPSelectionComplete): "both" | "king" | "servant"
|
|||
: "king";
|
||||
}
|
||||
|
||||
function renderVerbSelection(vs: T.VerbSelectionComplete, person: T.Person, objectPerson: T.Person | undefined): T.VerbRendered {
|
||||
type VerbBlocks = | [T.PerfectiveHeadBlock, T.VerbRenderedBlock] // verb w perfective split
|
||||
| [T.VerbRenderedBlock] // verb w/out perfective split
|
||||
| [T.PerfectParticipleBlock, T.PerfectEquativeBlock] // perfect verb
|
||||
| [T.ModalVerbBlock, T.ModalVerbKedulPart] // modal verb
|
||||
|
||||
function renderVerbSelection(vs: T.VerbSelectionComplete, person: T.Person, objectPerson: T.Person | undefined): {
|
||||
verbBlocks: VerbBlocks
|
||||
hasBa: boolean,
|
||||
} {
|
||||
const v = vs.dynAuxVerb || vs.verb;
|
||||
const conjugations = conjugateVerb(v.entry, v.complement);
|
||||
// TODO: error handle this?
|
||||
|
@ -118,14 +379,73 @@ function renderVerbSelection(vs: T.VerbSelectionComplete, person: T.Person, obje
|
|||
// will default to transitive
|
||||
? conjugations.transitive
|
||||
: "stative" in conjugations
|
||||
// TODO: option to manually select stative/dynamic
|
||||
? conjugations.stative
|
||||
: conjugations;
|
||||
return {
|
||||
const { ps: { head, rest }, hasBa } = getPsVerbConjugation(conj, vs, person, objectPerson);
|
||||
const vrb: T.VerbRenderedBlock = {
|
||||
type: "verb",
|
||||
block: {
|
||||
...vs,
|
||||
ps: rest,
|
||||
person,
|
||||
...getPsVerbConjugation(conj, vs, person, objectPerson),
|
||||
hasBa,
|
||||
},
|
||||
};
|
||||
const verbBlocks = [
|
||||
...(head ? (
|
||||
vs.isCompound === "stative" ? [{
|
||||
type: "verbComplement",
|
||||
complement: head,
|
||||
} as T.VerbComplementBlock] : [{
|
||||
type: "perfectiveHead",
|
||||
ps: head,
|
||||
} as T.PerfectiveHeadBlock]
|
||||
) : [] as [T.VerbComplementBlock] | [T.PerfectiveHeadBlock] | []),
|
||||
...splitUpIfModal(vrb),
|
||||
] as VerbBlocks;
|
||||
const perfectStuff = isPerfectTense(vrb.block.tense) ? getPerfectStuff(rest, vrb) : undefined;
|
||||
return {
|
||||
verbBlocks: perfectStuff ? perfectStuff : verbBlocks,
|
||||
hasBa,
|
||||
};
|
||||
}
|
||||
|
||||
function splitUpIfModal(v: T.VerbRenderedBlock): [T.VerbRenderedBlock] | [T.ModalVerbBlock, T.ModalVerbKedulPart] {
|
||||
if (!isModalTense(v.block.tense)) {
|
||||
return [v];
|
||||
}
|
||||
const [vrb, k] = splitOffLeapfrogWordFull(v.block.ps);
|
||||
return [
|
||||
{
|
||||
type: "modalVerbBlock",
|
||||
ps: vrb,
|
||||
verb: v,
|
||||
},
|
||||
{
|
||||
type: "modalVerbKedulPart",
|
||||
// sadly just for type safety - the conjugator always just gives us the short form
|
||||
ps: getShort(k),
|
||||
verb: v,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function getPerfectStuff(v: T.SingleOrLengthOpts<T.PsString[]>, vrb: T.VerbRenderedBlock): [T.PerfectParticipleBlock, T.PerfectEquativeBlock] {
|
||||
const [p, eq] = splitOffLeapfrogWordFull(v);
|
||||
return [
|
||||
{
|
||||
type: "perfectParticipleBlock",
|
||||
ps: p,
|
||||
person: vrb.block.person,
|
||||
verb: vrb,
|
||||
},
|
||||
{
|
||||
type: "perfectEquativeBlock",
|
||||
// TODO: right now the conjugator just always spits out the short form of the equative - would be nice to have both
|
||||
ps: getShort(eq),
|
||||
person: vrb.block.person,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function getPsVerbConjugation(conj: T.VerbConjugation, vs: T.VerbSelectionComplete, person: T.Person, objectPerson: T.Person | undefined): {
|
||||
|
@ -156,7 +476,20 @@ function getPsVerbConjugation(conj: T.VerbConjugation, vs: T.VerbSelectionComple
|
|||
ps,
|
||||
};
|
||||
}
|
||||
return { hasBa, ps: { head: undefined, rest: verbForm }};
|
||||
return { hasBa, ps: { head: undefined, rest: removeBaFromForm(verbForm) }};
|
||||
}
|
||||
|
||||
function removeBaFromForm(f: T.SingleOrLengthOpts<T.PsString[]>): T.SingleOrLengthOpts<T.PsString[]> {
|
||||
if ("long" in f) {
|
||||
return {
|
||||
long: removeBaFromForm(f.long) as T.PsString[],
|
||||
short: removeBaFromForm(f.short) as T.PsString[],
|
||||
...f.mini ? {
|
||||
mini: removeBaFromForm(f.mini) as T.PsString[],
|
||||
} : {},
|
||||
};
|
||||
}
|
||||
return f.map(removeBa);
|
||||
}
|
||||
|
||||
function getVerbFromBlock(block: T.SingleOrLengthOpts<T.VerbBlock | T.ImperativeBlock>, person: T.Person): T.SingleOrLengthOpts<T.PsString[]> {
|
||||
|
|
|
@ -1,120 +0,0 @@
|
|||
import * as T from "../../types";
|
||||
import {
|
||||
makePsString,
|
||||
} from "../accent-and-ps-utils";
|
||||
import {
|
||||
concatPsString,
|
||||
} from "../p-text-helpers";
|
||||
|
||||
type SegmentDescriptions = {
|
||||
isVerbHead?: boolean,
|
||||
isOoOrWaaHead?: boolean,
|
||||
isVerbRest?: boolean,
|
||||
isMiniPronoun?: number,
|
||||
isKid?: boolean,
|
||||
// TODO: Simplify to just isKidAfterHead?
|
||||
isKidBetweenHeadAndRest?: boolean,
|
||||
isNu?: boolean,
|
||||
isBa?: boolean,
|
||||
}
|
||||
|
||||
type SDT = keyof SegmentDescriptions;
|
||||
export type Segment = { ps: T.PsString[] } & SegmentDescriptions & {
|
||||
adjust: (o: { ps?: T.PsString | T.PsString[] | ((ps: T.PsString) => T.PsString), desc?: SDT[] }) => Segment,
|
||||
};
|
||||
|
||||
|
||||
export function makeSegment(
|
||||
input: T.PsString | T.PsString[],
|
||||
options?: (keyof SegmentDescriptions | 1 | 2 | 3)[],
|
||||
): Segment {
|
||||
const ps: T.PsString[] = Array.isArray(input)
|
||||
? input
|
||||
: [input];
|
||||
return {
|
||||
ps: Array.isArray(ps) ? ps : [ps],
|
||||
...options && options.reduce((all, curr) => ({
|
||||
...all,
|
||||
...typeof curr === "number" ? {
|
||||
isMiniPronoun: curr,
|
||||
} : {
|
||||
[curr]: true,
|
||||
},
|
||||
}), {}),
|
||||
adjust: function(o): Segment {
|
||||
return {
|
||||
...this,
|
||||
...o.ps ? {
|
||||
// TODO: is this ok with the adjectives?
|
||||
ps: Array.isArray(o.ps)
|
||||
? o.ps
|
||||
: "p" in o.ps
|
||||
? [o.ps]
|
||||
: this.ps.map(o.ps)
|
||||
} : {},
|
||||
...o.desc && o.desc.reduce((all, curr) => ({
|
||||
...all,
|
||||
[curr]: true,
|
||||
}), {}),
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function combineSegments(loe: (Segment | " " | "" | T.PsString)[], spaces?: "spaces"): T.PsString[] {
|
||||
const first = loe[0];
|
||||
const rest = loe.slice(1);
|
||||
if (!rest.length) {
|
||||
if (typeof first === "string" || !("ps" in first)) {
|
||||
throw new Error("can't end with a spacer");
|
||||
}
|
||||
return first.ps;
|
||||
}
|
||||
return combineSegments(rest, spaces).flatMap(r => (
|
||||
(typeof first === "object" && "ps" in first)
|
||||
? first.ps.map(f => (
|
||||
spaces ? concatPsString(f, " ", r) : concatPsString(f, r)
|
||||
))
|
||||
: [concatPsString(first, r)]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function flattenLengths(r: T.SingleOrLengthOpts<T.PsString[] | T.PsString>): T.PsString[] {
|
||||
if ("long" in r) {
|
||||
return Object.values(r).flat();
|
||||
}
|
||||
if (Array.isArray(r)) {
|
||||
return r;
|
||||
}
|
||||
return [r];
|
||||
}
|
||||
|
||||
export function putKidsInKidsSection(segments: Segment[], kids: Segment[]): Segment[] {
|
||||
const first = segments[0];
|
||||
const rest = segments.slice(1);
|
||||
return [
|
||||
first,
|
||||
// TODO: simplify to just isKidAfterHead ??
|
||||
...(first.isVerbHead && rest[0] && rest[0].isVerbRest)
|
||||
? kids.map(k => k.adjust({ desc: ["isKidBetweenHeadAndRest"] }))
|
||||
: kids,
|
||||
...rest,
|
||||
];
|
||||
}
|
||||
|
||||
export function splitOffLeapfrogWord(psVs: T.PsString[]): [T.PsString[], T.PsString[]] {
|
||||
return psVs.reduce((tp, ps) => {
|
||||
const pWords = ps.p.split(" ");
|
||||
const fWords = ps.f.split(" ");
|
||||
const beginning = makePsString(
|
||||
pWords.slice(0, -1).join(" "),
|
||||
fWords.slice(0, -1).join(" "),
|
||||
);
|
||||
const end = makePsString(
|
||||
pWords.slice(-1).join(" "),
|
||||
fWords.slice(-1).join(" "),
|
||||
);
|
||||
return [[...tp[0], beginning], [...tp[1], end]];
|
||||
}, [[], []] as [T.PsString[], T.PsString[]]);
|
||||
}
|
|
@ -155,7 +155,7 @@ export function getTenseFromVerbSelection(vs: T.VerbSelection): T.VerbTense | T.
|
|||
return "perfectiveFutureModal";
|
||||
}
|
||||
if (tn === "perfectivePast") {
|
||||
return "perfectiveFutureModal";
|
||||
return "perfectivePastModal";
|
||||
}
|
||||
if (tn === "imperfectivePast") {
|
||||
return "imperfectivePastModal";
|
||||
|
|
|
@ -1006,14 +1006,6 @@ function getPassiveStemPerfectiveSplit(stem: T.OptionalPersonInflections<T.Lengt
|
|||
|
||||
function getPassiveRootPerfectiveSplit(root: T.OptionalPersonInflections<T.LengthOptions<T.PsString>>, splitInfo: T.SplitInfo): T.SplitInfo {
|
||||
const si = "long" in splitInfo ? splitInfo.long : splitInfo;
|
||||
// if ("long" in splitInfo) {
|
||||
// return {
|
||||
// // @ts-ignore
|
||||
// short: getPassiveRootPerfectiveSplit(root, splitInfo.long, "short"),
|
||||
// // @ts-ignore
|
||||
// long: getPassiveRootPerfectiveSplit(root, splitInfo.long, "long"),
|
||||
// };
|
||||
// }
|
||||
if ("mascSing" in si) {
|
||||
if (!("mascSing" in root)) throw new Error("persInflections doesn't match perfective split");
|
||||
return {
|
||||
|
|
|
@ -15,7 +15,6 @@ import {
|
|||
import {
|
||||
getVerbInfo,
|
||||
} from "./lib/verb-info";
|
||||
import ConjugationViewer from "./components/ConjugationViewer";
|
||||
import InflectionsTable from "./components/InflectionsTable";
|
||||
import Pashto from "./components/Pashto";
|
||||
import Phonetics from "./components/Phonetics";
|
||||
|
@ -113,7 +112,7 @@ import {
|
|||
} from "./lib/misc-helpers";
|
||||
import {
|
||||
flattenLengths,
|
||||
} from "./lib/phrase-building/segment";
|
||||
} from "./lib/phrase-building/compile";
|
||||
import {
|
||||
simplifyPhonetics,
|
||||
} from "./lib/simplify-phonetics";
|
||||
|
@ -235,7 +234,6 @@ export {
|
|||
// COMPONENTS
|
||||
EPExplorer,
|
||||
VPExplorer,
|
||||
ConjugationViewer, // TODO: Deprecated - remove
|
||||
Examples,
|
||||
VerbFormDisplay,
|
||||
VerbTable,
|
||||
|
|
51
src/types.ts
51
src/types.ts
|
@ -505,7 +505,6 @@ export type Pattern6FemEntry<T extends FemNounEntry> = T & { __brand3: "non anim
|
|||
export type NonInflecting<T> = T & { __brand3: "non-inflecting" };
|
||||
|
||||
export type Entry = NounEntry | AdjectiveEntry | AdverbEntry | VerbEntry;
|
||||
export type RenderedVPSBlock = (Rendered<SubjectSelectionComplete> | Rendered<ObjectSelectionComplete> | Rendered<APSelection>);
|
||||
// TODO: make this Rendered<VPSelectionComplete> with recursive Rendered<>
|
||||
export type VPRendered = {
|
||||
type: "VPRendered",
|
||||
|
@ -514,8 +513,8 @@ export type VPRendered = {
|
|||
isPast: boolean,
|
||||
isTransitive: boolean,
|
||||
isCompound: "stative" | "dynamic" | false,
|
||||
blocks: RenderedVPSBlock[],
|
||||
verb: VerbRendered,
|
||||
blocks: Block[][],
|
||||
kids: Kid[],
|
||||
englishBase?: string[],
|
||||
form: FormVersion,
|
||||
whatsAdjustable: "both" | "king" | "servant",
|
||||
|
@ -838,7 +837,7 @@ export type EquativeRendered = EquativeSelection & {
|
|||
|
||||
export type EPRendered = {
|
||||
type: "EPRendered",
|
||||
blocks: Block[],
|
||||
blocks: Block[][],
|
||||
kids: Kid[],
|
||||
englishBase?: string[],
|
||||
omitSubject: boolean,
|
||||
|
@ -866,12 +865,54 @@ export type EntryLookupPortal<X extends VerbEntry | DictionaryEntry> = {
|
|||
}
|
||||
|
||||
export type EquativeBlock = { type: "equative", equative: EquativeRendered };
|
||||
export type VerbComplementBlock = {
|
||||
type: "verbComplement",
|
||||
complement: PsString,
|
||||
};
|
||||
export type PerfectParticipleBlock = {
|
||||
type: "perfectParticipleBlock",
|
||||
ps: SingleOrLengthOpts<PsString[]>,
|
||||
verb: VerbRenderedBlock,
|
||||
person: Person,
|
||||
};
|
||||
export type PerfectEquativeBlock = {
|
||||
type: "perfectEquativeBlock",
|
||||
ps: PsString[],
|
||||
person: Person,
|
||||
};
|
||||
export type ModalVerbBlock = {
|
||||
type: "modalVerbBlock",
|
||||
ps: SingleOrLengthOpts<PsString[]>,
|
||||
verb: VerbRenderedBlock,
|
||||
};
|
||||
export type ModalVerbKedulPart = {
|
||||
type: "modalVerbKedulPart",
|
||||
ps: PsString[],
|
||||
verb: VerbRenderedBlock,
|
||||
};
|
||||
export type PerfectiveHeadBlock = { type: "perfectiveHead", ps: PsString };
|
||||
export type VerbRenderedBlock = {
|
||||
type: "verb",
|
||||
block: Omit<VerbSelectionComplete, "object"> & {
|
||||
hasBa: boolean,
|
||||
ps: SingleOrLengthOpts<PsString[]>,
|
||||
person: Person,
|
||||
},
|
||||
};
|
||||
|
||||
export type Block =
|
||||
| Rendered<SubjectSelectionComplete>
|
||||
| Rendered<ObjectSelectionComplete>
|
||||
| Rendered<APSelection>
|
||||
| Rendered<PredicateSelectionComplete>
|
||||
| { type: "nu" }
|
||||
| PerfectParticipleBlock
|
||||
| PerfectEquativeBlock
|
||||
| ModalVerbBlock
|
||||
| ModalVerbKedulPart
|
||||
| { type: "negative", imperative: boolean }
|
||||
| PerfectiveHeadBlock
|
||||
| VerbRenderedBlock
|
||||
| VerbComplementBlock
|
||||
| EquativeBlock;
|
||||
|
||||
export type Kid =
|
||||
|
|
Loading…
Reference in New Issue