wohoo blocks working
This commit is contained in:
parent
560387d969
commit
96b91e6336
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@lingdocs/pashto-inflector",
|
"name": "@lingdocs/pashto-inflector",
|
||||||
"version": "2.9.8",
|
"version": "2.9.9",
|
||||||
"author": "lingdocs.com",
|
"author": "lingdocs.com",
|
||||||
"description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations",
|
"description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations",
|
||||||
"homepage": "https://verbs.lingdocs.com",
|
"homepage": "https://verbs.lingdocs.com",
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { getShort } from "../../lib/p-text-helpers";
|
import { getShort } from "../lib/p-text-helpers";
|
||||||
import * as T from "../../types";
|
import * as T from "../types";
|
||||||
import Examples from "../Examples";
|
import Examples from "./Examples";
|
||||||
|
|
||||||
function EPTextDisplay({ compiled, opts, justify, onlyOne }: {
|
function CompiledPTextDisplay({ compiled, opts, justify, onlyOne }: {
|
||||||
compiled: {
|
compiled: {
|
||||||
ps: T.SingleOrLengthOpts<T.PsString[]>;
|
ps: T.SingleOrLengthOpts<T.PsString[]>;
|
||||||
e?: string[] | undefined;
|
e?: string[] | undefined;
|
||||||
|
@ -30,4 +30,4 @@ function EPTextDisplay({ compiled, opts, justify, onlyOne }: {
|
||||||
</div>;
|
</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,
|
getEnglishFromRendered,
|
||||||
} from "../../lib/phrase-building/np-tools";
|
} from "../../lib/phrase-building/np-tools";
|
||||||
import { getEnglishPersonInfo } from "../../library";
|
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,
|
opts: T.TextOptions,
|
||||||
block: T.Block,
|
block: T.Block,
|
||||||
|
king?: "subject" | "object" | undefined,
|
||||||
|
script: "p" | "f";
|
||||||
}) {
|
}) {
|
||||||
if ("equative" in block) {
|
if ("equative" in block) {
|
||||||
return <EquativeBlock opts={opts} eq={block.equative} />;
|
return <EquativeBlock opts={opts} eq={block.equative} script={script} />;
|
||||||
}
|
}
|
||||||
if (block.type === "AP") {
|
if (block.type === "AP") {
|
||||||
const english = getEnglishFromRendered(block);
|
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") {
|
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") {
|
if (block.type === "predicateSelection") {
|
||||||
const english = getEnglishFromRendered(block.selection);
|
const english = getEnglishFromRendered(block.selection);
|
||||||
return <div className="text-center">
|
return <div className="text-center">
|
||||||
<div><strong>Predicate</strong></div>
|
<div><strong>Predicate</strong></div>
|
||||||
{block.selection.type === "EQComp"
|
{block.selection.type === "EQComp"
|
||||||
? <EqCompBlock opts={opts} comp={block.selection.selection} />
|
? <EqCompBlock opts={opts} comp={block.selection.selection} script={script} />
|
||||||
: <NPBlock opts={opts} english={english}>{block.selection}</NPBlock>}
|
: <NPBlock opts={opts} english={english} script={script}>{block.selection}</NPBlock>}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
if (block.type === "nu") {
|
if (block.type === "negative") {
|
||||||
return <NUBlock opts={opts} />
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Block;
|
export default Block;
|
||||||
|
|
||||||
function NUBlock({ opts }: {
|
function Border({ children, extraClassName, padding }: { children: JSX.Element | JSX.Element[] | string, extraClassName?: string, padding?: string }) {
|
||||||
opts: T.TextOptions,
|
return <div
|
||||||
}) {
|
className={`d-flex flex-row justify-content-center align-items-center ${extraClassName ? extraClassName : ""}`}
|
||||||
return <div className="text-center">
|
|
||||||
<div
|
|
||||||
className={classNames("d-flex flex-row justify-content-center align-items-center")}
|
|
||||||
style={{
|
style={{
|
||||||
border: "2px solid black",
|
border: "2px solid black",
|
||||||
padding: "1rem",
|
padding: padding ? padding : "1rem",
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
nu
|
<>{children}</>
|
||||||
</div>
|
</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>
|
<div>Neg.</div>
|
||||||
<EnglishBelow>not</EnglishBelow>
|
<EnglishBelow>{imperative ? "don't" : "not"}</EnglishBelow>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function EquativeBlock({ opts, eq }: {
|
function EquativeBlock({ opts, eq, script }: {
|
||||||
opts: T.TextOptions,
|
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">
|
return <div className="text-center">
|
||||||
<div
|
{"long" in eq.ps && <div className="clickable small mb-1" onClick={changeLength}>{length}</div>}
|
||||||
className={classNames("d-flex flex-row justify-content-center align-items-center")}
|
<Border>
|
||||||
style={{
|
{getLength(eq.ps, length)[0][script]}
|
||||||
border: "2px solid black",
|
</Border>
|
||||||
padding: "1rem",
|
|
||||||
textAlign: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{"short" in eq.ps ? eq.ps.short[0].f : eq.ps[0].f}
|
|
||||||
</div>
|
|
||||||
<div>Equative</div>
|
<div>Equative</div>
|
||||||
<EnglishBelow>{"="}</EnglishBelow>
|
<EnglishBelow>{getEnglishPersonInfo(eq.person, "short")}</EnglishBelow>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SubjectBlock({ opts, np }: {
|
function SubjectBlock({ opts, np, role, script }: {
|
||||||
opts: T.TextOptions,
|
opts: T.TextOptions,
|
||||||
np: T.Rendered<T.NPSelection>,
|
np: T.Rendered<T.NPSelection>,
|
||||||
|
role: "king" | "servant" | undefined,
|
||||||
|
script: "p" | "f",
|
||||||
}) {
|
}) {
|
||||||
const english = getEnglishFromRendered(np);
|
const english = getEnglishFromRendered(np);
|
||||||
return <div className="text-center">
|
return <div className="text-center">
|
||||||
<div><strong>Subject</strong></div>
|
<div><strong>Subject</strong>{role ? roleIcon[role] : ""}</div>
|
||||||
<NPBlock opts={opts} english={english}>{np}</NPBlock>
|
<NPBlock opts={opts} english={english} script={script}>{np}</NPBlock>
|
||||||
</div>;
|
</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,
|
opts: T.TextOptions,
|
||||||
comp: T.Rendered<T.EqCompSelection["selection"]>,
|
comp: T.Rendered<T.EqCompSelection["selection"]>,
|
||||||
}) {
|
}) {
|
||||||
|
@ -95,16 +251,9 @@ function EqCompBlock({ opts, comp }: {
|
||||||
adj: T.Rendered<T.AdjectiveSelection>,
|
adj: T.Rendered<T.AdjectiveSelection>,
|
||||||
}) {
|
}) {
|
||||||
return <div className="text-center">
|
return <div className="text-center">
|
||||||
<div
|
<Border>
|
||||||
className={classNames("d-flex flex-row justify-content-center align-items-center")}
|
{adj.ps[0][script]}
|
||||||
style={{
|
</Border>
|
||||||
border: "2px solid black",
|
|
||||||
padding: "1rem",
|
|
||||||
textAlign: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{adj.ps[0].f}
|
|
||||||
</div>
|
|
||||||
<div>Adj.</div>
|
<div>Adj.</div>
|
||||||
<EnglishBelow>{adj.e}</EnglishBelow>
|
<EnglishBelow>{adj.e}</EnglishBelow>
|
||||||
</div>;
|
</div>;
|
||||||
|
@ -115,16 +264,9 @@ function EqCompBlock({ opts, comp }: {
|
||||||
adv: T.Rendered<T.LocativeAdverbSelection>,
|
adv: T.Rendered<T.LocativeAdverbSelection>,
|
||||||
}) {
|
}) {
|
||||||
return <div className="text-center">
|
return <div className="text-center">
|
||||||
<div
|
<Border>
|
||||||
className={classNames("d-flex flex-row justify-content-center align-items-center")}
|
{adv.ps[0][script]}
|
||||||
style={{
|
</Border>
|
||||||
border: "2px solid black",
|
|
||||||
padding: "1rem",
|
|
||||||
textAlign: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{adv.ps[0].f}
|
|
||||||
</div>
|
|
||||||
<div>Loc. Adv.</div>
|
<div>Loc. Adv.</div>
|
||||||
<EnglishBelow>{adv.e}</EnglishBelow>
|
<EnglishBelow>{adv.e}</EnglishBelow>
|
||||||
</div>;
|
</div>;
|
||||||
|
@ -137,89 +279,78 @@ function EqCompBlock({ opts, comp }: {
|
||||||
: comp.type === "loc. adv."
|
: comp.type === "loc. adv."
|
||||||
? <LocAdvBlock opts={opts} adv={comp} />
|
? <LocAdvBlock opts={opts} adv={comp} />
|
||||||
: <div>
|
: <div>
|
||||||
<Sandwich opts={opts} sandwich={comp} />
|
<Sandwich opts={opts} sandwich={comp} script={script} />
|
||||||
<div>Sandwich</div>
|
<div>Sandwich</div>
|
||||||
<EnglishBelow>{comp.e}</EnglishBelow>
|
<EnglishBelow>{comp.e}</EnglishBelow>
|
||||||
</div>}
|
</div>}
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function APBlock({ opts, children, english }: {
|
export function APBlock({ opts, children, english, script }: {
|
||||||
opts: T.TextOptions,
|
opts: T.TextOptions,
|
||||||
children: T.Rendered<T.APSelection>,
|
children: T.Rendered<T.APSelection>,
|
||||||
english?: string,
|
english?: string,
|
||||||
|
script: "p" | "f",
|
||||||
}) {
|
}) {
|
||||||
const ap = children;
|
const ap = children;
|
||||||
if (ap.selection.type === "adverb") {
|
if (ap.selection.type === "adverb") {
|
||||||
return <div className="text-center">
|
return <div className="text-center">
|
||||||
<div
|
<Border>
|
||||||
className={classNames("d-flex flex-row justify-content-center align-items-center")}
|
{ap.selection.ps[0][script]}
|
||||||
style={{
|
</Border>
|
||||||
border: "2px solid black",
|
|
||||||
padding: "1rem",
|
|
||||||
textAlign: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ap.selection.ps[0].f}
|
|
||||||
</div>
|
|
||||||
<div>AP</div>
|
<div>AP</div>
|
||||||
<EnglishBelow>{english}</EnglishBelow>
|
<EnglishBelow>{english}</EnglishBelow>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
return <div>
|
return <div>
|
||||||
<Sandwich opts={opts} sandwich={ap.selection} />
|
<Sandwich opts={opts} sandwich={ap.selection} script={script} />
|
||||||
<div>AP</div>
|
<div>AP</div>
|
||||||
<EnglishBelow>{english}</EnglishBelow>
|
<EnglishBelow>{english}</EnglishBelow>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Sandwich({ opts, sandwich }: {
|
function Sandwich({ opts, sandwich, script }: {
|
||||||
opts: T.TextOptions,
|
opts: T.TextOptions,
|
||||||
sandwich: T.Rendered<T.SandwichSelection<T.Sandwich>>,
|
sandwich: T.Rendered<T.SandwichSelection<T.Sandwich>>,
|
||||||
|
script: "p" | "f",
|
||||||
}) {
|
}) {
|
||||||
return <div className="text-center">
|
return <div className="text-center">
|
||||||
<div className="text-center">Sandwich 🥪</div>
|
<div className="text-center">Sandwich 🥪</div>
|
||||||
<div
|
<Border padding="0.75rem 0.5rem 0.25rem 0.5rem">
|
||||||
className={classNames("d-flex flex-row justify-content-center align-items-center")}
|
<div className={`d-flex flex-row${script === "p" ? "-reverse" : ""} justify-content-between align-items-end`}>
|
||||||
style={{
|
<Possesors opts={opts} script={script}>{sandwich.inside.selection.type !== "pronoun" ? sandwich.inside.selection.possesor : undefined}</Possesors>
|
||||||
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>
|
|
||||||
<div className="mr-2 ml-1 mb-1"><strong>{sandwich.before ? sandwich.before.f : ""}</strong></div>
|
<div className="mr-2 ml-1 mb-1"><strong>{sandwich.before ? sandwich.before.f : ""}</strong></div>
|
||||||
<div>
|
<div>
|
||||||
<NPBlock opts={opts} inside>{sandwich.inside}</NPBlock>
|
<NPBlock opts={opts} inside script={script}>{sandwich.inside}</NPBlock>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-2 mr-1 mb-1"><strong>{sandwich.after ? sandwich.after.f : ""}</strong></div>
|
<div className="ml-2 mr-1 mb-1"><strong>{sandwich.after ? sandwich.after.f : ""}</strong></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Border>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NPBlock({ opts, children, inside, english }: {
|
export function NPBlock({ opts, children, inside, english, script }: {
|
||||||
opts: T.TextOptions,
|
opts: T.TextOptions,
|
||||||
children: T.Rendered<T.NPSelection>,
|
children: T.Rendered<T.NPSelection>,
|
||||||
inside?: boolean,
|
inside?: boolean,
|
||||||
english?: string,
|
english?: string,
|
||||||
|
script: "p" | "f",
|
||||||
}) {
|
}) {
|
||||||
const np = children;
|
const np = children;
|
||||||
const hasPossesor = !!(np.selection.type !== "pronoun" && np.selection.possesor && !np.selection.possesor.shrunken);
|
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">
|
return <div className="text-center">
|
||||||
<div
|
<Border
|
||||||
className={classNames("d-flex flex-row justify-content-center align-items-center", { "pt-2": !inside && hasPossesor })}
|
extraClassName={`!inside && hasPossesor ? "pt-2" : ""`}
|
||||||
style={{
|
padding={inside ? "0.3rem" : hasPossesor ? "0.5rem 0.8rem 0.25rem 0.8rem" : "1rem"}
|
||||||
border: "2px solid black",
|
|
||||||
padding: inside ? "0.3rem" : hasPossesor ? "0.5rem 1rem 0.25rem 1rem" : "1rem",
|
|
||||||
textAlign: "center",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{!inside && <Possesors opts={opts}>{np.selection.type !== "pronoun" ? np.selection.possesor : undefined}</Possesors>}
|
{el}
|
||||||
<Adjectives opts={opts}>{np.selection.adjectives}</Adjectives>
|
</Border>
|
||||||
<div> {np.selection.ps[0].f}</div>
|
|
||||||
</div>
|
|
||||||
<div className={inside ? "small" : ""}>
|
<div className={inside ? "small" : ""}>
|
||||||
NP
|
NP
|
||||||
{!inside ? <>
|
{!inside ? <>
|
||||||
|
@ -227,13 +358,14 @@ export function NPBlock({ opts, children, inside, english }: {
|
||||||
<span className="text-muted small">({getEnglishPersonInfo(np.selection.person, "short")})</span>
|
<span className="text-muted small">({getEnglishPersonInfo(np.selection.person, "short")})</span>
|
||||||
</> : <></>}
|
</> : <></>}
|
||||||
</div>
|
</div>
|
||||||
<EnglishBelow>{english}</EnglishBelow>
|
{!inside && <EnglishBelow>{english}</EnglishBelow>}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
function Possesors({ opts, children }: {
|
function Possesors({ opts, children, script }: {
|
||||||
opts: T.TextOptions,
|
opts: T.TextOptions,
|
||||||
children: { shrunken: boolean, np: T.Rendered<T.NPSelection> } | undefined,
|
children: { shrunken: boolean, np: T.Rendered<T.NPSelection> } | undefined,
|
||||||
|
script: "p" | "f",
|
||||||
}) {
|
}) {
|
||||||
if (!children) {
|
if (!children) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -241,18 +373,18 @@ function Possesors({ opts, children }: {
|
||||||
if (children.shrunken) {
|
if (children.shrunken) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const contraction = checkForContraction(children.np);
|
const contraction = checkForContraction(children.np, script);
|
||||||
return <div className="d-flex flex-row mr-1 align-items-end" style={{
|
return <div className={`d-flex flex-row${script === "p" ? "-reverse" : ""} mr-1 align-items-end`} style={{
|
||||||
marginBottom: "0.5rem",
|
marginBottom: "0.5rem",
|
||||||
borderBottom: "1px solid grey",
|
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>
|
<div>
|
||||||
{contraction && <div className="mb-1">({contraction})</div>}
|
{contraction && <div className="mb-1">({contraction})</div>}
|
||||||
<div className={classNames("d-flex", "flex-row", "align-items-center", { "text-muted": contraction })}>
|
<div className={classNames("d-flex", (script === "f" ? "flex-row" : "flex-row-reverse"), "align-items-center", { "text-muted": contraction })}>
|
||||||
<div className="mr-1 pb-2">du</div>
|
<div className="mx-1 pb-2">{script === "p" ? "د" : "du"}</div>
|
||||||
<div>
|
<div>
|
||||||
<NPBlock opts={opts} inside>{children.np}</NPBlock>
|
<NPBlock script={script} opts={opts} inside>{children.np}</NPBlock>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -260,32 +392,19 @@ function Possesors({ opts, children }: {
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkForContraction(np: T.Rendered<T.NPSelection>): string | undefined {
|
function Adjectives({ opts, children, script }: {
|
||||||
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 }: {
|
|
||||||
opts: T.TextOptions,
|
opts: T.TextOptions,
|
||||||
children: T.Rendered<T.AdjectiveSelection>[] | undefined,
|
children: T.Rendered<T.AdjectiveSelection>[] | undefined,
|
||||||
|
script: "p" | "f",
|
||||||
}) {
|
}) {
|
||||||
if (!children) {
|
if (!children) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
const c = script === "p"
|
||||||
|
? children.reverse()
|
||||||
|
: children;
|
||||||
return <em className="mr-1">
|
return <em className="mr-1">
|
||||||
{children.map(a => a.ps[0].f).join(" ")}{` `}
|
{c.map(a => a.ps[0][script]).join(" ")}{` `}
|
||||||
</em>
|
</em>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,3 +415,21 @@ function EnglishBelow({ children: e }: { children: string | undefined }) {
|
||||||
height: "1rem",
|
height: "1rem",
|
||||||
}}>{e ? e : ""}</div>;
|
}}>{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 { baParticle } from "../../lib/grammar-units";
|
||||||
import * as T from "../../types";
|
import * as T from "../../types";
|
||||||
|
import Pashto from "../Pashto";
|
||||||
import Phonetics from "../Phonetics";
|
import Phonetics from "../Phonetics";
|
||||||
|
|
||||||
function KidDisplay({ opts, kid }: {
|
function KidDisplay({ opts, kid, script }: {
|
||||||
opts: T.TextOptions,
|
opts: T.TextOptions,
|
||||||
kid: T.Kid,
|
kid: T.Kid,
|
||||||
|
script: "p" | "f",
|
||||||
}) {
|
}) {
|
||||||
|
const ps = kid.type === "ba"
|
||||||
|
? baParticle
|
||||||
|
: kid.ps;
|
||||||
return <div className="mx-1">
|
return <div className="mx-1">
|
||||||
{kid.type === "ba"
|
{script === "p"
|
||||||
? <Phonetics opts={opts}>{baParticle}</Phonetics>
|
? <Pashto opts={opts}>{ps}</Pashto>
|
||||||
: <Phonetics opts={opts}>{kid.ps}</Phonetics>}
|
: <Phonetics opts={opts}>{ps}</Phonetics>}
|
||||||
</div>
|
</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 ButtonSelect from "../ButtonSelect";
|
||||||
import { getPredicateSelectionFromBlocks, getSubjectSelection, getSubjectSelectionFromBlocks } from "../../lib/phrase-building/blocks-utils";
|
import { getPredicateSelectionFromBlocks, getSubjectSelection, getSubjectSelectionFromBlocks } from "../../lib/phrase-building/blocks-utils";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import EPTextDisplay from "./EPTextDisplay";
|
import CompiledPTextDisplay from "../CompiledPTextDisplay";
|
||||||
import EPBlocksDisplay from "./EPBlocksDisplay";
|
import EPBlocksDisplay from "../RenderedBlocksDisplay";
|
||||||
type Mode = "text" | "blocks";
|
import ModeSelect, { Mode, ScriptSelect } from "../DisplayModeSelect";
|
||||||
|
import { useStickyState } from "../../library";
|
||||||
|
|
||||||
function EPDisplay({ eps, opts, setOmitSubject, justify, onlyOne }: {
|
function EPDisplay({ eps, opts, setOmitSubject, justify, onlyOne }: {
|
||||||
eps: T.EPSelectionState,
|
eps: T.EPSelectionState,
|
||||||
|
@ -16,6 +17,7 @@ function EPDisplay({ eps, opts, setOmitSubject, justify, onlyOne }: {
|
||||||
onlyOne?: boolean,
|
onlyOne?: boolean,
|
||||||
}) {
|
}) {
|
||||||
const [mode, setMode] = useState<Mode>("text");
|
const [mode, setMode] = useState<Mode>("text");
|
||||||
|
const [script, setScript] = useStickyState<"p" | "f">("f", "blockScriptChoice");
|
||||||
const EP = completeEPSelection(eps);
|
const EP = completeEPSelection(eps);
|
||||||
const subject = getSubjectSelection(eps.blocks);
|
const subject = getSubjectSelection(eps.blocks);
|
||||||
|
|
||||||
|
@ -36,7 +38,10 @@ function EPDisplay({ eps, opts, setOmitSubject, justify, onlyOne }: {
|
||||||
const renderedPredicate = getPredicateSelectionFromBlocks(rendered.blocks).selection;
|
const renderedPredicate = getPredicateSelectionFromBlocks(rendered.blocks).selection;
|
||||||
return <div className="text-center pt-3">
|
return <div className="text-center pt-3">
|
||||||
<div className="mb-2 d-flex flex-row justify-content-between align-items-center">
|
<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} />
|
<ModeSelect value={mode} onChange={setMode} />
|
||||||
|
{mode === "blocks" && <ScriptSelect value={script} onChange={setScript} />}
|
||||||
|
</div>
|
||||||
{setOmitSubject !== false ? <ButtonSelect
|
{setOmitSubject !== false ? <ButtonSelect
|
||||||
small
|
small
|
||||||
value={(eps.omitSubject ? "true" : "false") as "true" | "false"}
|
value={(eps.omitSubject ? "true" : "false") as "true" | "false"}
|
||||||
|
@ -49,8 +54,8 @@ function EPDisplay({ eps, opts, setOmitSubject, justify, onlyOne }: {
|
||||||
<div />
|
<div />
|
||||||
</div>
|
</div>
|
||||||
{mode === "text"
|
{mode === "text"
|
||||||
? <EPTextDisplay opts={opts} compiled={result} justify={justify} onlyOne={onlyOne} />
|
? <CompiledPTextDisplay opts={opts} compiled={result} justify={justify} onlyOne={onlyOne} />
|
||||||
: <EPBlocksDisplay opts={opts} rendered={rendered} justify={justify} />}
|
: <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"}`}>
|
{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>)}
|
{(onlyOne ? [result.e[0]] : result.e).map((e, i) => <div key={i}>{e}</div>)}
|
||||||
</div>}
|
</div>}
|
||||||
|
@ -63,14 +68,4 @@ function EPDisplay({ eps, opts, setOmitSubject, justify, onlyOne }: {
|
||||||
</div>
|
</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;
|
export default EPDisplay;
|
|
@ -4,7 +4,7 @@ import {
|
||||||
personNumber,
|
personNumber,
|
||||||
} from "../../lib/misc-helpers";
|
} from "../../lib/misc-helpers";
|
||||||
import { isUnisexNounEntry } from "../../lib/type-predicates";
|
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";
|
import { adjustSubjectSelection, getSubjectSelection, insertNewAP, removeAP, setAP, shiftBlock } from "../../lib/phrase-building/blocks-utils";
|
||||||
|
|
||||||
export type EpsReducerAction = {
|
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 {
|
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 (error) {
|
||||||
if (sendAlert) sendAlert(error);
|
if (sendAlert) sendAlert(error);
|
||||||
return old;
|
return old;
|
||||||
|
|
|
@ -1,56 +1,55 @@
|
||||||
import { compileVP } from "../../lib/phrase-building/compile";
|
import { compileVP } from "../../lib/phrase-building/compile";
|
||||||
import * as T from "../../types";
|
import * as T from "../../types";
|
||||||
import AbbreviationFormSelector from "./AbbreviationFormSelector";
|
import AbbreviationFormSelector from "./AbbreviationFormSelector";
|
||||||
import Examples from "../Examples";
|
|
||||||
import { getObjectSelection, getSubjectSelection } from "../../lib/phrase-building/blocks-utils";
|
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 }: {
|
function VPDisplay({ VPS, opts, setForm, justify, onlyOne }: {
|
||||||
VP: T.VPSelectionState | T.VPRendered,
|
VPS: T.VPSelectionState,
|
||||||
opts: T.TextOptions,
|
opts: T.TextOptions,
|
||||||
setForm: (form: T.FormVersion) => void,
|
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">
|
return <div className="lead text-muted text-center mt-4">
|
||||||
{(() => {
|
{(() => {
|
||||||
const subject = getSubjectSelection(VP.blocks).selection;
|
const subject = getSubjectSelection(VPS.blocks).selection;
|
||||||
const object = getObjectSelection(VP.blocks).selection;
|
const object = getObjectSelection(VPS.blocks).selection;
|
||||||
if (subject === undefined || object || undefined) {
|
if (subject === undefined || object === undefined) {
|
||||||
return `Choose NP${((subject === undefined) && (object === undefined)) ? "s " : ""} to make a phrase`;
|
return `Choose NP${((subject === undefined) && (object === undefined)) ? "s " : ""} to make a phrase`;
|
||||||
}
|
}
|
||||||
return `Choose/remove AP to complete the phrase`;
|
return `Choose/remove AP to complete the phrase`;
|
||||||
})()}
|
})()}
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
const result = compileVP(VP, { ...VP.form });
|
const rendered = renderVP(VP);
|
||||||
|
const result = compileVP(rendered, rendered.form);
|
||||||
return <div className="text-center mt-1">
|
return <div className="text-center mt-1">
|
||||||
<AbbreviationFormSelector
|
<AbbreviationFormSelector
|
||||||
adjustable={VP.whatsAdjustable}
|
adjustable={rendered.whatsAdjustable}
|
||||||
form={VP.form}
|
form={rendered.form}
|
||||||
onChange={setForm}
|
onChange={setForm}
|
||||||
/>
|
/>
|
||||||
{"long" in result.ps ?
|
<div className="d-flex flex-row">
|
||||||
<div>
|
<ModeSelect value={mode} onChange={setMode} />
|
||||||
{/* <div className="h6">Long Verb:</div> */}
|
{mode === "blocks" && <ScriptSelect value={script} onChange={setScript} />}
|
||||||
<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>
|
</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 && <div className="text-muted mt-3">
|
||||||
{result.e.map((e, i) => <div key={i}>{e}</div>)}
|
{result.e.map((e, i) => <div key={i}>{e}</div>)}
|
||||||
</div>}
|
</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;
|
export default VPDisplay;
|
|
@ -15,7 +15,6 @@ import VPExplorerExplanationModal, { roleIcon } from "./VPExplorerExplanationMod
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import LZString from "lz-string";
|
import LZString from "lz-string";
|
||||||
import { vpsReducer } from "./vps-reducer";
|
import { vpsReducer } from "./vps-reducer";
|
||||||
import { getShrunkenServant } from "../../lib/phrase-building/compile";
|
|
||||||
import APPicker from "../ap-picker/APPicker";
|
import APPicker from "../ap-picker/APPicker";
|
||||||
import autoAnimate from "@formkit/auto-animate";
|
import autoAnimate from "@formkit/auto-animate";
|
||||||
import { getObjectSelection, getSubjectSelection, isNoObject } from "../../lib/phrase-building/blocks-utils";
|
import { getObjectSelection, getSubjectSelection, isNoObject } from "../../lib/phrase-building/blocks-utils";
|
||||||
|
@ -44,7 +43,7 @@ function VPExplorer(props: {
|
||||||
props.loaded
|
props.loaded
|
||||||
? props.loaded
|
? props.loaded
|
||||||
: savedVps => makeVPSelectionState(props.verb, savedVps),
|
: savedVps => makeVPSelectionState(props.verb, savedVps),
|
||||||
"vpsState14",
|
"vpsState15",
|
||||||
flashMessage,
|
flashMessage,
|
||||||
);
|
);
|
||||||
const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">(
|
const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">(
|
||||||
|
@ -143,7 +142,7 @@ function VPExplorer(props: {
|
||||||
const VPS = completeVPSelection(vps);
|
const VPS = completeVPSelection(vps);
|
||||||
const phraseIsComplete = !!VPS;
|
const phraseIsComplete = !!VPS;
|
||||||
const rendered = VPS ? renderVP(VPS) : undefined;
|
const rendered = VPS ? renderVP(VPS) : undefined;
|
||||||
const servantIsShrunk = !!(rendered ? getShrunkenServant(rendered) : undefined);
|
const servantIsShrunk = includesShrunkenServant(rendered?.kids);
|
||||||
function toggleServantShrink() {
|
function toggleServantShrink() {
|
||||||
adjustVps({
|
adjustVps({
|
||||||
type: "toggle servant shrink",
|
type: "toggle servant shrink",
|
||||||
|
@ -257,7 +256,12 @@ function VPExplorer(props: {
|
||||||
: (vps.verb && block?.type === "objectSelection" && block.selection !== "none")
|
: (vps.verb && block?.type === "objectSelection" && block.selection !== "none")
|
||||||
? <div className="my-2" style={{ background: (servantIsShrunk && roles.servant === "object") ? shrunkenBackground : "inherit" }}>
|
? <div className="my-2" style={{ background: (servantIsShrunk && roles.servant === "object") ? shrunkenBackground : "inherit" }}>
|
||||||
{(typeof block.selection === "number")
|
{(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
|
: <NPPicker
|
||||||
phraseIsComplete={phraseIsComplete}
|
phraseIsComplete={phraseIsComplete}
|
||||||
heading={roles.king === "object"
|
heading={roles.king === "object"
|
||||||
|
@ -295,7 +299,7 @@ function VPExplorer(props: {
|
||||||
</div>
|
</div>
|
||||||
</div>}
|
</div>}
|
||||||
{mode === "phrases" && <VPDisplay
|
{mode === "phrases" && <VPDisplay
|
||||||
VP={rendered ? rendered : vps}
|
VPS={vps}
|
||||||
opts={props.opts}
|
opts={props.opts}
|
||||||
setForm={handleSetForm}
|
setForm={handleSetForm}
|
||||||
/>}
|
/>}
|
||||||
|
@ -351,3 +355,10 @@ function getVPSFromUrl(): T.VPSelectionState | undefined {
|
||||||
return JSON.parse(decoded) as T.VPSelectionState;
|
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 TensePicker from "./TensePicker";
|
||||||
import Keyframes from "../Keyframes";
|
import Keyframes from "../Keyframes";
|
||||||
import energyDrink from "./energy-drink.jpg";
|
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 { concatPsString } from "../../lib/p-text-helpers";
|
||||||
import { isImperativeTense } from "../../lib/type-predicates";
|
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 = ["✅", '🤓', "✅", '😊', "🌹", "✅", "✅", '🥳', "👏", "✅", "💯", "😎", "✅", "👍"];
|
const correctEmoji = ["✅", '🤓', "✅", '😊', "🌹", "✅", "✅", '🥳', "👏", "✅", "💯", "😎", "✅", "👍"];
|
||||||
|
|
||||||
|
@ -113,8 +113,8 @@ function VPExplorerQuiz(props: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const rendered = renderVP(quizState.vps);
|
const rendered = renderVP(quizState.vps);
|
||||||
const subject: T.Rendered<T.NPSelection> = getRenderedSubjectSelection(rendered.blocks).selection;
|
const subject: T.Rendered<T.NPSelection> = getSubjectSelectionFromBlocks(rendered.blocks).selection;
|
||||||
const object = getRenderedObjectSelection(rendered.blocks).selection;
|
const object = getObjectSelectionFromBlocks(rendered.blocks).selection;
|
||||||
const { e } = compileVP(rendered, { removeKing: false, shrinkServant: false });
|
const { e } = compileVP(rendered, { removeKing: false, shrinkServant: false });
|
||||||
return <div className="mt-4">
|
return <div className="mt-4">
|
||||||
<ProgressBar quizState={quizState} />
|
<ProgressBar quizState={quizState} />
|
||||||
|
@ -346,18 +346,17 @@ function tickQuizState(startingWith: T.VPSelectionComplete | QuizState): QuizSta
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBlanksAnswer(vps: T.VPSelectionComplete): { ps: T.PsString[], withBa: boolean } {
|
function getBlanksAnswer(vps: T.VPSelectionComplete): { ps: T.PsString[], withBa: boolean } {
|
||||||
const { verb } = renderVP(vps);
|
const { verb, perfectiveHead } = getVerbAndHeadFromBlocks(renderVP(vps).blocks);
|
||||||
const { head, rest } = verb.ps;
|
const ps = flattenLengths(verb.block.ps).map(x => {
|
||||||
const ps = flattenLengths(rest).map(x => {
|
|
||||||
const y = removeBa(x);
|
const y = removeBa(x);
|
||||||
if (head) {
|
if (perfectiveHead) {
|
||||||
return concatPsString(head, y);
|
return concatPsString(perfectiveHead.ps, y);
|
||||||
}
|
}
|
||||||
return y;
|
return y;
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
ps,
|
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 { kawulStat } from "./irregular-conjugations";
|
||||||
import * as T from "../types";
|
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 = [
|
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[];
|
].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] {
|
export function splitOffLeapfrogWord(ps: T.PsString): [T.PsString, T.PsString] {
|
||||||
const pWords = ps.p.split(" ");
|
const pWords = ps.p.split(" ");
|
||||||
const fWords = ps.f.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 {
|
export function getLength<U>(x: T.SingleOrLengthOpts<U>, length: "long" | "short" | "mini"): U {
|
||||||
return ("long" in x) ? x[length] : x;
|
if ("long" in x) {
|
||||||
|
const s = x[length];
|
||||||
|
return s ? s : x.short;
|
||||||
|
}
|
||||||
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLong<U>(x: T.SingleOrLengthOpts<U>): U {
|
export function getLong<U>(x: T.SingleOrLengthOpts<U>): U {
|
||||||
|
|
|
@ -11,24 +11,61 @@ export function getSubjectSelection(blocks: T.EPSBlock[] | T.EPSBlockComplete[]
|
||||||
return b.block;
|
return b.block;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSubjectSelectionFromBlocks(blocks: T.Block[]): T.Rendered<T.SubjectSelectionComplete> {
|
export function getSubjectSelectionFromBlocks(blocks: T.Block[][]): T.Rendered<T.SubjectSelectionComplete> {
|
||||||
const b = blocks.find(f => f.type === "subjectSelection");
|
const b = blocks[0].find(f => f.type === "subjectSelection");
|
||||||
if (!b || b.type !== "subjectSelection") {
|
if (!b || b.type !== "subjectSelection") {
|
||||||
throw new Error("subjectSelection not found in blocks");
|
throw new Error("subjectSelection not found in blocks");
|
||||||
}
|
}
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPredicateSelectionFromBlocks(blocks: T.Block[]): T.Rendered<T.PredicateSelectionComplete> {
|
export function getObjectSelectionFromBlocks(blocks: T.Block[][]): T.Rendered<T.ObjectSelectionComplete> {
|
||||||
const b = blocks.find(f => f.type === "predicateSelection");
|
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") {
|
if (!b || b.type !== "predicateSelection") {
|
||||||
throw new Error("predicateSelection not found in blocks");
|
throw new Error("predicateSelection not found in blocks");
|
||||||
}
|
}
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAPsFromBlocks(blocks: T.Block[]): T.Rendered<T.APSelection>[] {
|
export function getAPsFromBlocks(blocks: T.Block[][]): T.Rendered<T.APSelection>[] {
|
||||||
return blocks.filter(b => b.type === "AP") as 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;
|
export function getObjectSelection(blocks: T.VPSBlockComplete[]): T.ObjectSelectionComplete;
|
||||||
|
@ -41,23 +78,6 @@ export function getObjectSelection(blocks: T.VPSBlock[] | T.VPSBlockComplete[]):
|
||||||
return b.block;
|
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[] {
|
export function makeEPSBlocks(): T.EPSBlock[] {
|
||||||
return [
|
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");
|
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");
|
const i = blocks.findIndex(b => b.type === "equative");
|
||||||
if (i === -1) throw new Error("equative block not found in EPRendered");
|
if (i === -1) throw new Error("equative block not found in EPRendered");
|
||||||
const eq = blocks[i];
|
const eq = blocks[i];
|
||||||
|
@ -220,15 +241,62 @@ export function specifyEquativeLength(blocks: T.Block[], length: "long" | "short
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return adjusted;
|
return adjusted;
|
||||||
|
}
|
||||||
|
return blocksWVars.map(specify);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hasEquativeWithLengths(blocks: T.Block[]): boolean {
|
export function specifyVerbLength(blocksWVars: T.Block[][], length: "long" | "short" | "mini"): T.Block[][] {
|
||||||
const equative = blocks.find(x => x.type === "equative");
|
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) throw new Error("equative not found in blocks");
|
||||||
if (equative.type !== "equative") throw new Error("error finding equative in blocks");
|
if (equative.type !== "equative") throw new Error("error finding equative in blocks");
|
||||||
return "long" in equative.equative.ps;
|
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[] {
|
function arrayMove<X>(ar: X[], old_index: number, new_index: number): X[] {
|
||||||
const arr = [...ar];
|
const arr = [...ar];
|
||||||
const new_i = (new_index >= arr.length)
|
const new_i = (new_index >= arr.length)
|
||||||
|
|
|
@ -1,33 +1,27 @@
|
||||||
import * as T from "../../types";
|
import * as T from "../../types";
|
||||||
import {
|
import {
|
||||||
concatPsString, getLong,
|
concatPsString, getLong, getShort,
|
||||||
} from "../p-text-helpers";
|
} from "../p-text-helpers";
|
||||||
import {
|
import { negativeParticle } from "../../lib/grammar-units";
|
||||||
Segment,
|
|
||||||
makeSegment,
|
|
||||||
flattenLengths,
|
|
||||||
combineSegments,
|
|
||||||
splitOffLeapfrogWord,
|
|
||||||
putKidsInKidsSection as oldPutKidsInKidsSection,
|
|
||||||
} from "./segment";
|
|
||||||
import {
|
|
||||||
removeAccents,
|
|
||||||
} from "../accent-helpers";
|
|
||||||
import * as grammarUnits from "../grammar-units";
|
import * as grammarUnits from "../grammar-units";
|
||||||
import {
|
import {
|
||||||
removeBa,
|
|
||||||
removeDuplicates,
|
removeDuplicates,
|
||||||
} from "./vp-tools";
|
} from "./vp-tools";
|
||||||
import { isImperativeTense, isModalTense, isPerfectTense } from "../type-predicates";
|
|
||||||
import { getEnglishFromRendered, getPashtoFromRendered } from "./np-tools";
|
import { getEnglishFromRendered, getPashtoFromRendered } from "./np-tools";
|
||||||
import { getVerbBlockPosFromPerson } from "../misc-helpers";
|
|
||||||
import { pronouns } from "../grammar-units";
|
|
||||||
import { completeEPSelection, renderEP } from "./render-ep";
|
import { completeEPSelection, renderEP } from "./render-ep";
|
||||||
import { completeVPSelection } from "./vp-tools";
|
import { completeVPSelection } from "./vp-tools";
|
||||||
import { renderVP } from "./render-vp";
|
import { renderVP } from "./render-vp";
|
||||||
import { getAPsFromBlocks, getPredicateSelectionFromBlocks, getRenderedObjectSelection, getRenderedSubjectSelection, getSubjectSelectionFromBlocks, hasEquativeWithLengths, specifyEquativeLength } from "./blocks-utils";
|
import {
|
||||||
|
getAPsFromBlocks,
|
||||||
// TODO: GET BLANKING WORKING!
|
getObjectSelectionFromBlocks,
|
||||||
|
getPredicateSelectionFromBlocks,
|
||||||
|
getSubjectSelectionFromBlocks,
|
||||||
|
getVerbFromBlocks,
|
||||||
|
hasEquativeWithLengths,
|
||||||
|
hasVerbWithLengths,
|
||||||
|
specifyEquativeLength,
|
||||||
|
specifyVerbLength,
|
||||||
|
} from "./blocks-utils";
|
||||||
|
|
||||||
const blank: T.PsString = {
|
const blank: T.PsString = {
|
||||||
p: "______",
|
p: "______",
|
||||||
|
@ -37,228 +31,31 @@ type BlankoutOptions = { equative?: boolean, ba?: boolean, kidsSection?: boolean
|
||||||
|
|
||||||
const kidsBlank: T.PsString = { p: "___", f: "___" };
|
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 = {
|
// function compilePs({ blocks, kids, verb: { head, rest }, VP }: CompilePsInput): T.SingleOrLengthOpts<T.PsString[]> {
|
||||||
blocks: Segment[],
|
// if ("long" in rest) {
|
||||||
kids: Segment[],
|
// return {
|
||||||
verb: {
|
// long: compilePs({ blocks, verb: { head, rest: rest.long }, VP, kids }) as T.PsString[],
|
||||||
head: T.PsString | undefined,
|
// short: compilePs({ blocks, verb: { head, rest: rest.short }, VP, kids }) as T.PsString[],
|
||||||
rest: T.SingleOrLengthOpts<T.PsString[]>,
|
// ...rest.mini ? {
|
||||||
},
|
// mini: compilePs({ blocks, verb: { head, rest: rest.mini }, VP, kids }) as T.PsString[],
|
||||||
VP: T.VPRendered,
|
// } : {},
|
||||||
}
|
// };
|
||||||
|
// }
|
||||||
|
// const verbWNegativeVersions = arrangeVerbWNegative(head, rest, VP.verb);
|
||||||
|
|
||||||
function compilePs({ blocks, kids, verb: { head, rest }, VP }: CompilePsInput): T.SingleOrLengthOpts<T.PsString[]> {
|
// // put together all the different possible permutations based on:
|
||||||
if ("long" in rest) {
|
// // a. potential different versions of where the nu goes
|
||||||
return {
|
// return removeDuplicates(verbWNegativeVersions.flatMap((verbSegments) => {
|
||||||
long: compilePs({ blocks, verb: { head, rest: rest.long }, VP, kids }) as T.PsString[],
|
// // for each permutation of the possible ordering of NPs and Verb + nu
|
||||||
short: compilePs({ blocks, verb: { head, rest: rest.short }, VP, kids }) as T.PsString[],
|
// // 1. put in kids in the kids section
|
||||||
...rest.mini ? {
|
// const segments = oldPutKidsInKidsSection([...blocks, ...verbSegments], kids);
|
||||||
mini: compilePs({ blocks, verb: { head, rest: rest.mini }, VP, kids }) as T.PsString[],
|
// // 2. space out the words properly
|
||||||
} : {},
|
// const withProperSpaces = addSpacesBetweenSegments(segments);
|
||||||
};
|
// // 3. throw it all together into a PsString for each permutation
|
||||||
}
|
// return combineSegments(withProperSpaces);
|
||||||
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 }),
|
|
||||||
]] : [],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function compileEP(EP: T.EPRendered): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string[] };
|
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[] };
|
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)) {
|
if (hasEquativeWithLengths(blocks)) {
|
||||||
return {
|
return {
|
||||||
long: compileEPPs(specifyEquativeLength(blocks, "long"), kids, omitSubject, blankOut) as T.PsString[],
|
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)
|
const subjectPerson = getSubjectSelectionFromBlocks(blocks)
|
||||||
.selection.selection.person;
|
.selection.selection.person;
|
||||||
const blocksWKids = putKidsInKidsSection(
|
const blocksWKids = putKidsInKidsSection(
|
||||||
omitSubject ? blocks.filter(b => b.type !== "subjectSelection") : blocks,
|
omitSubject ? blocks.map(blks => blks.filter(b => b.type !== "subjectSelection")) : blocks,
|
||||||
kids,
|
kids,
|
||||||
!!blankOut?.kidsSection
|
!!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));
|
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 first = pieces[0];
|
||||||
|
const next = pieces[1];
|
||||||
const rest = pieces.slice(1);
|
const rest = pieces.slice(1);
|
||||||
const firstPs = ("p" in first)
|
const firstPs = ("p" in first)
|
||||||
? [first]
|
? [first]
|
||||||
|
@ -301,10 +162,18 @@ function combineIntoText(pieces: (T.Block | T.Kid | T.PsString)[], subjectPerson
|
||||||
if (!rest.length) {
|
if (!rest.length) {
|
||||||
return firstPs;
|
return firstPs;
|
||||||
}
|
}
|
||||||
return combineIntoText(rest, subjectPerson, blankOut).flatMap(r => (
|
return combine(rest).flatMap(r => (
|
||||||
firstPs.map(fPs => concatPsString(fPs, " ", 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[] {
|
function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsString[] {
|
||||||
|
@ -314,8 +183,10 @@ function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsSt
|
||||||
if (piece.type === "mini-pronoun") {
|
if (piece.type === "mini-pronoun") {
|
||||||
return [piece.ps];
|
return [piece.ps];
|
||||||
}
|
}
|
||||||
if (piece.type === "nu") {
|
if (piece.type === "negative") {
|
||||||
return [{ p: "نه", f: "nú" }];
|
return [
|
||||||
|
negativeParticle[piece.imperative ? "imperative" : "nonImperative"],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
if (piece.type === "equative") {
|
if (piece.type === "equative") {
|
||||||
// length will already be specified in compileEPPs - this is just for type safety
|
// 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") {
|
if (piece.type === "subjectSelection" || piece.type === "predicateSelection") {
|
||||||
return getPashtoFromRendered(piece.selection, subjectPerson);
|
return getPashtoFromRendered(piece.selection, subjectPerson);
|
||||||
}
|
}
|
||||||
// if (piece.type === "AP") {
|
if (piece.type === "AP") {
|
||||||
return getPashtoFromRendered(piece, subjectPerson);
|
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) => {
|
return getAPsFromBlocks(blocks).reduce((accum, curr) => {
|
||||||
const e = getEnglishFromRendered(curr);
|
const e = getEnglishFromRendered(curr);
|
||||||
if (!e) return accum;
|
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)[]) {
|
function putKidsInKidsSection(blocksWVars: T.Block[][], kids: T.Kid[], enforceKidsSectionBlankout: boolean): (T.Block | T.Kid | T.PsString)[][] {
|
||||||
const APs = blocks.filter(x => x.type !== "subjectSelection") as T.Rendered<T.APSelection>[];
|
function insert(blocks: T.Block[]): (T.Block | T.Kid | T.PsString)[] {
|
||||||
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)[] {
|
|
||||||
const first = blocks[0];
|
const first = blocks[0];
|
||||||
const rest = blocks.slice(1);
|
const rest = blocks.slice(1);
|
||||||
return [
|
return [
|
||||||
|
@ -354,59 +250,22 @@ function putKidsInKidsSection(blocks: T.Block[], kids: T.Kid[], enforceKidsSecti
|
||||||
...enforceKidsSectionBlankout ? [kidsBlank] : kids,
|
...enforceKidsSectionBlankout ? [kidsBlank] : kids,
|
||||||
...rest,
|
...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) });
|
return blocksWVars.map(insert);
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function compileEnglishVP(VP: T.VPRendered): string[] | undefined {
|
function compileEnglishVP(VP: T.VPRendered): string[] | undefined {
|
||||||
function insertEWords(e: string, { subject, object, APs }: { subject: string, object?: string, APs: string }): string {
|
function insertEWords(e: string, { subject, object, APs }: { subject: string, object?: string, APs: string }): string {
|
||||||
return e.replace("$SUBJ", subject).replace("$OBJ", object || "") + APs;
|
return e.replace("$SUBJ", subject).replace("$OBJ", object || "") + APs;
|
||||||
}
|
}
|
||||||
const engSubj = getRenderedSubjectSelection(VP.blocks).selection;
|
const engSubj = getSubjectSelectionFromBlocks(VP.blocks).selection;
|
||||||
const obj = getRenderedObjectSelection(VP.blocks).selection;
|
const obj = getObjectSelectionFromBlocks(VP.blocks).selection;
|
||||||
const engObj = typeof obj === "object"
|
const engObj = typeof obj === "object"
|
||||||
? obj
|
? obj
|
||||||
: obj === "none"
|
: obj === "none"
|
||||||
? ""
|
? ""
|
||||||
: undefined;
|
: undefined;
|
||||||
const engAPs = getEnglishAPs(VP.blocks);
|
const engAPs = getEngAPs(VP.blocks);
|
||||||
// require all English parts for making the English phrase
|
// require all English parts for making the English phrase
|
||||||
return (VP.englishBase && engSubj && engObj !== undefined)
|
return (VP.englishBase && engSubj && engObj !== undefined)
|
||||||
? VP.englishBase.map(e => insertEWords(e, {
|
? VP.englishBase.map(e => insertEWords(e, {
|
||||||
|
@ -438,27 +297,7 @@ function compileEnglishEP(EP: T.EPRendered): string[] | undefined {
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function orderKidsSection(kids: Segment[]): Segment[] {
|
export function checkForMiniPronounsError(s: T.EPSelectionState | T.VPSelectionState): undefined | string {
|
||||||
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 {
|
|
||||||
function findDuplicateMiniP(mp: T.MiniPronoun[]): T.MiniPronoun | undefined {
|
function findDuplicateMiniP(mp: T.MiniPronoun[]): T.MiniPronoun | undefined {
|
||||||
const duplicates = mp.filter((item, index) => (
|
const duplicates = mp.filter((item, index) => (
|
||||||
mp.findIndex(m => item.ps.p === m.ps.p) !== 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];
|
return duplicates[0];
|
||||||
}
|
}
|
||||||
const kids = (() => {
|
const kids = (() => {
|
||||||
|
if ("predicate" in s) {
|
||||||
const EPS = completeEPSelection(s);
|
const EPS = completeEPSelection(s);
|
||||||
if (!EPS) return undefined;
|
if (!EPS) return undefined;
|
||||||
const { kids } = renderEP(EPS);
|
return renderEP(EPS).kids;
|
||||||
return kids;
|
};
|
||||||
|
const VPS = completeVPSelection(s);
|
||||||
|
if (!VPS) return undefined;
|
||||||
|
return renderVP(VPS).kids;
|
||||||
})();
|
})();
|
||||||
if (!kids) return undefined;
|
if (!kids) return undefined;
|
||||||
const miniPronouns = kids.filter(x => x.type === "mini-pronoun") as T.MiniPronoun[];
|
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;
|
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>[] {
|
function findPossesivesInNP(NP: T.Rendered<T.NPSelection> | T.ObjectNP | undefined): T.Rendered<T.NPSelection>[] {
|
||||||
if (NP === undefined) return [];
|
if (NP === undefined) return [];
|
||||||
if (typeof NP !== "object") return [];
|
if (typeof NP !== "object") return [];
|
||||||
|
@ -574,24 +356,13 @@ function findPossesivesInAdjective(a: T.Rendered<T.AdjectiveSelection>): T.Rende
|
||||||
return findPossesivesInNP(a.sandwich.inside);
|
return findPossesivesInNP(a.sandwich.inside);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function flattenLengths(r: T.SingleOrLengthOpts<T.PsString[] | T.PsString>): T.PsString[] {
|
||||||
// export function findPossesiveToShrinkInVP(VP: T.VPRendered): T.Rendered<T.NPSelection>[] {
|
if ("long" in r) {
|
||||||
// const obj: T.Rendered<T.NPSelection> | undefined = ("object" in VP && typeof VP.object === "object")
|
return Object.values(r).flat();
|
||||||
// ? 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;
|
|
||||||
}
|
}
|
||||||
const [row, col] = getVerbBlockPosFromPerson(np.selection.person);
|
if (Array.isArray(r)) {
|
||||||
return makeSegment(pronouns.mini[row][col], ["isKid", getFirstSecThird()]);
|
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,
|
getPersonFromNP,
|
||||||
} from "./vp-tools";
|
} from "./vp-tools";
|
||||||
import { renderNPSelection } from "./render-np";
|
import { renderNPSelection } from "./render-np";
|
||||||
import { getFirstSecThird, getPersonFromVerbForm } from "../../lib/misc-helpers";
|
import { getPersonFromVerbForm } from "../../lib/misc-helpers";
|
||||||
import { getVerbBlockPosFromPerson } from "../misc-helpers";
|
import { getVerbBlockPosFromPerson } from "../misc-helpers";
|
||||||
import { getEnglishWord } from "../get-english-word";
|
import { getEnglishWord } from "../get-english-word";
|
||||||
import { psStringFromEntry } from "../p-text-helpers";
|
import { psStringFromEntry } from "../p-text-helpers";
|
||||||
|
@ -13,7 +13,7 @@ import { renderAdjectiveSelection } from "./render-adj";
|
||||||
import { renderSandwich } from "./render-sandwich";
|
import { renderSandwich } from "./render-sandwich";
|
||||||
import { EPSBlocksAreComplete, getSubjectSelection } from "./blocks-utils";
|
import { EPSBlocksAreComplete, getSubjectSelection } from "./blocks-utils";
|
||||||
import { removeAccentsWLength } from "../accent-helpers";
|
import { removeAccentsWLength } from "../accent-helpers";
|
||||||
import { pronouns } from "../grammar-units";
|
import { findPossesivesToShrink, orderKids } from "./render-common";
|
||||||
|
|
||||||
export function renderEP(EP: T.EPSelectionComplete): T.EPRendered {
|
export function renderEP(EP: T.EPSelectionComplete): T.EPRendered {
|
||||||
const { kids, blocks, englishEquativePerson } = getEPSBlocksAndKids(EP);
|
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 subject = getSubjectSelection(EP.blocks).selection;
|
||||||
const subjectPerson = getPersonFromNP(subject);
|
const subjectPerson = getPersonFromNP(subject);
|
||||||
const commandingNP: T.NPSelection = subject.selection.type === "pronoun"
|
const commandingNP: T.NPSelection = subject.selection.type === "pronoun"
|
||||||
|
@ -39,7 +39,7 @@ function getEPSBlocksAndKids(EP: T.EPSelectionComplete): { kids: T.Kid[], blocks
|
||||||
: subject;
|
: subject;
|
||||||
const commandingPerson = getPersonFromNP(commandingNP);
|
const commandingPerson = getPersonFromNP(commandingNP);
|
||||||
const equative: T.EquativeBlock = { type: "equative", equative: renderEquative(EP.equative, commandingPerson) };
|
const equative: T.EquativeBlock = { type: "equative", equative: renderEquative(EP.equative, commandingPerson) };
|
||||||
const blocks: T.Block[] = [
|
const blocks: T.Block[][] = insertNegative([
|
||||||
...renderEPSBlocks(EP.blocks),
|
...renderEPSBlocks(EP.blocks),
|
||||||
{
|
{
|
||||||
type: "predicateSelection",
|
type: "predicateSelection",
|
||||||
|
@ -47,10 +47,9 @@ function getEPSBlocksAndKids(EP: T.EPSelectionComplete): { kids: T.Kid[], blocks
|
||||||
? renderNPSelection(EP.predicate.selection, false, false, "subject", "king")
|
? renderNPSelection(EP.predicate.selection, false, false, "subject", "king")
|
||||||
: renderEqCompSelection(EP.predicate.selection, commandingPerson),
|
: renderEqCompSelection(EP.predicate.selection, commandingPerson),
|
||||||
},
|
},
|
||||||
...EP.equative.negative ? [{ type: "nu" } as T.Block] : [],
|
equative,
|
||||||
EP.equative.negative ? removeAccontsFromEq(equative) : equative,
|
], EP.equative.negative);
|
||||||
];
|
const miniPronouns = findPossesivesToShrink(removeOrKeepSubject([...EP.blocks, EP.predicate], EP.omitSubject));
|
||||||
const miniPronouns = findPossesivesToShrink([...EP.blocks, EP.predicate], EP.omitSubject);
|
|
||||||
const kids: T.Kid[] = orderKids([
|
const kids: T.Kid[] = orderKids([
|
||||||
...equative.equative.hasBa ? [{ type: "ba" } as T.Kid] : [],
|
...equative.equative.hasBa ? [{ type: "ba" } as T.Kid] : [],
|
||||||
...miniPronouns,
|
...miniPronouns,
|
||||||
|
@ -64,92 +63,27 @@ function getEPSBlocksAndKids(EP: T.EPSelectionComplete): { kids: T.Kid[], blocks
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function orderKids(kids: T.Kid[]): T.Kid[] {
|
function insertNegative(blocks: T.Block[], negative: boolean): T.Block[][] {
|
||||||
const sorted = [...kids].sort((a, b) => {
|
if (!negative) return [blocks];
|
||||||
// ba first
|
const blocksA = removeAccentsFromEq(blocks);
|
||||||
if (a.type === "ba") return -1;
|
return [
|
||||||
// kinds lined up 1st 2nd 3rd person
|
[
|
||||||
if (a.type === "mini-pronoun" && b.type === "mini-pronoun") {
|
...blocksA.slice(0, blocks.length - 1),
|
||||||
const aPers = getFirstSecThird(a.person);
|
{ type: "negative", imperative: false },
|
||||||
const bPers = getFirstSecThird(b.person);
|
...blocksA.slice(-1), // last (equative)
|
||||||
if (aPers < bPers) {
|
],
|
||||||
return -1;
|
[
|
||||||
}
|
...blocksA.slice(0, blocks.length - 2),
|
||||||
if (aPers > bPers) {
|
{ type: "negative", imperative: false },
|
||||||
return 1;
|
...blocksA.slice(-1), // last (equative)
|
||||||
}
|
...blocksA.slice(-2, -1), // second last (predicate)
|
||||||
// TODO: is this enough?
|
],
|
||||||
return 0;
|
];
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
return sorted;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function findPossesivesToShrink(blocks: (T.EPSBlockComplete | T.SubjectSelectionComplete | T.PredicateSelectionComplete | T.APSelection)[], omitSubject: boolean): T.MiniPronoun[] {
|
function removeOrKeepSubject(blocks: (T.EPSBlockComplete | T.SubjectSelectionComplete | T.PredicateSelectionComplete | T.APSelection)[], omitSubject: boolean): (T.EPSBlockComplete | T.SubjectSelectionComplete | T.PredicateSelectionComplete | T.APSelection)[] {
|
||||||
return blocks.reduce((kids, item) => {
|
if (!omitSubject) return blocks;
|
||||||
const block = "block" in item ? item.block : item;
|
return blocks.filter(b => !("type" in b && b.type === "subjectSelection"));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getEquativeForm(tense: T.EquativeTense): { hasBa: boolean, form: T.SingleOrLengthOpts<T.VerbBlock> } {
|
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 {
|
function removeAccentsFromEq(blocks: T.Block[]): T.Block[] {
|
||||||
const [row, col] = getVerbBlockPosFromPerson(person);
|
return blocks.map((block) => {
|
||||||
return pronouns.mini[row][col][0];
|
if (block.type === "equative") {
|
||||||
}
|
const e: T.EquativeBlock = {
|
||||||
|
...block,
|
||||||
function removeAccontsFromEq(equ: T.EquativeBlock): T.EquativeBlock {
|
|
||||||
return {
|
|
||||||
...equ,
|
|
||||||
equative: {
|
equative: {
|
||||||
...equ.equative,
|
...block.equative,
|
||||||
ps: removeAccentsWLength(equ.equative.ps),
|
ps: removeAccentsWLength(block.equative.ps),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
return block;
|
||||||
|
});
|
||||||
}
|
}
|
|
@ -17,8 +17,8 @@ import { renderAdjectiveSelection } from "./render-adj";
|
||||||
import { isPattern5Entry, isAnimNounEntry } from "../type-predicates";
|
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, 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, inflected: boolean, inflectEnglish: boolean, role: "object", soRole: "servant" | "king" | "none"): T.Rendered<T.NPSelection>;
|
||||||
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: "subject" | "object", soRole: "servant" | "king" | "none"): T.Rendered<T.NPSelection> {
|
||||||
if (typeof NP !== "object") {
|
if (typeof NP !== "object") {
|
||||||
if (role !== "object") {
|
if (role !== "object") {
|
||||||
throw new Error("ObjectNP only allowed for objects");
|
throw new Error("ObjectNP only allowed for objects");
|
||||||
|
|
|
@ -7,8 +7,10 @@ import {
|
||||||
hasBaParticle,
|
hasBaParticle,
|
||||||
getLong,
|
getLong,
|
||||||
isImperativeBlock,
|
isImperativeBlock,
|
||||||
|
splitOffLeapfrogWordFull,
|
||||||
|
getShort,
|
||||||
} from "../p-text-helpers";
|
} from "../p-text-helpers";
|
||||||
import { removeAccents } from "../accent-helpers";
|
import { removeAccents, removeAccentsWLength } from "../accent-helpers";
|
||||||
import {
|
import {
|
||||||
getPersonFromNP,
|
getPersonFromNP,
|
||||||
removeBa,
|
removeBa,
|
||||||
|
@ -16,6 +18,8 @@ import {
|
||||||
getTenseVerbForm,
|
getTenseVerbForm,
|
||||||
} from "./vp-tools";
|
} from "./vp-tools";
|
||||||
import {
|
import {
|
||||||
|
isImperativeTense,
|
||||||
|
isModalTense,
|
||||||
isPattern4Entry,
|
isPattern4Entry,
|
||||||
isPerfectTense,
|
isPerfectTense,
|
||||||
} from "../type-predicates";
|
} from "../type-predicates";
|
||||||
|
@ -24,6 +28,7 @@ import { personGender } from "../../lib/misc-helpers";
|
||||||
import { renderNPSelection } from "./render-np";
|
import { renderNPSelection } from "./render-np";
|
||||||
import { getObjectSelection, getSubjectSelection } from "./blocks-utils";
|
import { getObjectSelection, getSubjectSelection } from "./blocks-utils";
|
||||||
import { renderAPSelection } from "./render-ap";
|
import { renderAPSelection } from "./render-ap";
|
||||||
|
import { findPossesivesToShrink, orderKids, getMiniPronounPs } from "./render-common";
|
||||||
|
|
||||||
// TODO: ISSUE GETTING SPLIT HEAD NOT MATCHING WITH FUTURE VERBS
|
// 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 inflectSubject = isPast && isTransitive && !isMascSingAnimatePattern4(subject);
|
||||||
const inflectObject = !isPast && isFirstOrSecondPersPronoun(object);
|
const inflectObject = !isPast && isFirstOrSecondPersPronoun(object);
|
||||||
// Render Elements
|
// Render Elements
|
||||||
|
const firstBlocks = renderVPBlocks(VP.blocks, {
|
||||||
|
inflectSubject,
|
||||||
|
inflectObject,
|
||||||
|
king,
|
||||||
|
});
|
||||||
|
const { verbBlocks, hasBa } = renderVerbSelection(VP.verb, kingPerson, objectPerson);
|
||||||
const b: T.VPRendered = {
|
const b: T.VPRendered = {
|
||||||
type: "VPRendered",
|
type: "VPRendered",
|
||||||
king,
|
king,
|
||||||
|
@ -54,12 +65,11 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
|
||||||
isPast,
|
isPast,
|
||||||
isTransitive,
|
isTransitive,
|
||||||
isCompound: VP.verb.isCompound,
|
isCompound: VP.verb.isCompound,
|
||||||
blocks: renderVPBlocks(VP.blocks, {
|
blocks: insertNegative([
|
||||||
inflectSubject,
|
...firstBlocks,
|
||||||
inflectObject,
|
...verbBlocks,
|
||||||
king,
|
], VP.verb.negative, isImperativeTense(VP.verb.tense)),
|
||||||
}),
|
kids: getVPKids(hasBa, VP.blocks, VP.form, king),
|
||||||
verb: renderVerbSelection(VP.verb, kingPerson, objectPerson),
|
|
||||||
englishBase: renderEnglishVPBase({
|
englishBase: renderEnglishVPBase({
|
||||||
subjectPerson,
|
subjectPerson,
|
||||||
object: VP.verb.isCompound === "dynamic" ? "none" : object,
|
object: VP.verb.isCompound === "dynamic" ? "none" : object,
|
||||||
|
@ -71,28 +81,271 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
|
||||||
return b;
|
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: {
|
function renderVPBlocks(blocks: T.VPSBlockComplete[], config: {
|
||||||
inflectSubject: boolean,
|
inflectSubject: boolean,
|
||||||
inflectObject: boolean,
|
inflectObject: boolean,
|
||||||
king: "subject" | "object",
|
king: "subject" | "object",
|
||||||
}): T.RenderedVPSBlock[] {
|
}): T.Block[] {
|
||||||
return blocks.map(({ block }): T.RenderedVPSBlock => {
|
return blocks.reduce((blocks, { block }): T.Block[] => {
|
||||||
if (block.type === "subjectSelection") {
|
if (block.type === "subjectSelection") {
|
||||||
return {
|
return [
|
||||||
|
...blocks,
|
||||||
|
{
|
||||||
type: "subjectSelection",
|
type: "subjectSelection",
|
||||||
selection: renderNPSelection(block.selection, config.inflectSubject, false, "subject", config.king === "subject" ? "king" : "servant"),
|
selection: renderNPSelection(block.selection, config.inflectSubject, false, "subject", config.king === "subject" ? "king" : "servant"),
|
||||||
}
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
if (block.type === "objectSelection") {
|
if (block.type === "objectSelection") {
|
||||||
const object = typeof block === "object" ? block.selection : block;
|
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");
|
const selection = renderNPSelection(object, config.inflectObject, true, "object", config.king === "object" ? "king" : "servant");
|
||||||
return {
|
return [
|
||||||
|
...blocks,
|
||||||
|
{
|
||||||
type: "objectSelection",
|
type: "objectSelection",
|
||||||
selection,
|
selection,
|
||||||
};
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
return renderAPSelection(block);
|
return [
|
||||||
});
|
...blocks,
|
||||||
|
renderAPSelection(block),
|
||||||
|
];
|
||||||
|
}, [] as T.Block[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function whatsAdjustable(VP: T.VPSelectionComplete): "both" | "king" | "servant" {
|
function whatsAdjustable(VP: T.VPSelectionComplete): "both" | "king" | "servant" {
|
||||||
|
@ -109,7 +362,15 @@ function whatsAdjustable(VP: T.VPSelectionComplete): "both" | "king" | "servant"
|
||||||
: "king";
|
: "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 v = vs.dynAuxVerb || vs.verb;
|
||||||
const conjugations = conjugateVerb(v.entry, v.complement);
|
const conjugations = conjugateVerb(v.entry, v.complement);
|
||||||
// TODO: error handle this?
|
// TODO: error handle this?
|
||||||
|
@ -118,14 +379,73 @@ function renderVerbSelection(vs: T.VerbSelectionComplete, person: T.Person, obje
|
||||||
// will default to transitive
|
// will default to transitive
|
||||||
? conjugations.transitive
|
? conjugations.transitive
|
||||||
: "stative" in conjugations
|
: "stative" in conjugations
|
||||||
// TODO: option to manually select stative/dynamic
|
|
||||||
? conjugations.stative
|
? conjugations.stative
|
||||||
: conjugations;
|
: conjugations;
|
||||||
return {
|
const { ps: { head, rest }, hasBa } = getPsVerbConjugation(conj, vs, person, objectPerson);
|
||||||
|
const vrb: T.VerbRenderedBlock = {
|
||||||
|
type: "verb",
|
||||||
|
block: {
|
||||||
...vs,
|
...vs,
|
||||||
|
ps: rest,
|
||||||
person,
|
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): {
|
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,
|
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[]> {
|
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";
|
return "perfectiveFutureModal";
|
||||||
}
|
}
|
||||||
if (tn === "perfectivePast") {
|
if (tn === "perfectivePast") {
|
||||||
return "perfectiveFutureModal";
|
return "perfectivePastModal";
|
||||||
}
|
}
|
||||||
if (tn === "imperfectivePast") {
|
if (tn === "imperfectivePast") {
|
||||||
return "imperfectivePastModal";
|
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 {
|
function getPassiveRootPerfectiveSplit(root: T.OptionalPersonInflections<T.LengthOptions<T.PsString>>, splitInfo: T.SplitInfo): T.SplitInfo {
|
||||||
const si = "long" in splitInfo ? splitInfo.long : 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 si) {
|
||||||
if (!("mascSing" in root)) throw new Error("persInflections doesn't match perfective split");
|
if (!("mascSing" in root)) throw new Error("persInflections doesn't match perfective split");
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -15,7 +15,6 @@ import {
|
||||||
import {
|
import {
|
||||||
getVerbInfo,
|
getVerbInfo,
|
||||||
} from "./lib/verb-info";
|
} from "./lib/verb-info";
|
||||||
import ConjugationViewer from "./components/ConjugationViewer";
|
|
||||||
import InflectionsTable from "./components/InflectionsTable";
|
import InflectionsTable from "./components/InflectionsTable";
|
||||||
import Pashto from "./components/Pashto";
|
import Pashto from "./components/Pashto";
|
||||||
import Phonetics from "./components/Phonetics";
|
import Phonetics from "./components/Phonetics";
|
||||||
|
@ -113,7 +112,7 @@ import {
|
||||||
} from "./lib/misc-helpers";
|
} from "./lib/misc-helpers";
|
||||||
import {
|
import {
|
||||||
flattenLengths,
|
flattenLengths,
|
||||||
} from "./lib/phrase-building/segment";
|
} from "./lib/phrase-building/compile";
|
||||||
import {
|
import {
|
||||||
simplifyPhonetics,
|
simplifyPhonetics,
|
||||||
} from "./lib/simplify-phonetics";
|
} from "./lib/simplify-phonetics";
|
||||||
|
@ -235,7 +234,6 @@ export {
|
||||||
// COMPONENTS
|
// COMPONENTS
|
||||||
EPExplorer,
|
EPExplorer,
|
||||||
VPExplorer,
|
VPExplorer,
|
||||||
ConjugationViewer, // TODO: Deprecated - remove
|
|
||||||
Examples,
|
Examples,
|
||||||
VerbFormDisplay,
|
VerbFormDisplay,
|
||||||
VerbTable,
|
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 NonInflecting<T> = T & { __brand3: "non-inflecting" };
|
||||||
|
|
||||||
export type Entry = NounEntry | AdjectiveEntry | AdverbEntry | VerbEntry;
|
export type Entry = NounEntry | AdjectiveEntry | AdverbEntry | VerbEntry;
|
||||||
export type RenderedVPSBlock = (Rendered<SubjectSelectionComplete> | Rendered<ObjectSelectionComplete> | Rendered<APSelection>);
|
|
||||||
// TODO: make this Rendered<VPSelectionComplete> with recursive Rendered<>
|
// TODO: make this Rendered<VPSelectionComplete> with recursive Rendered<>
|
||||||
export type VPRendered = {
|
export type VPRendered = {
|
||||||
type: "VPRendered",
|
type: "VPRendered",
|
||||||
|
@ -514,8 +513,8 @@ export type VPRendered = {
|
||||||
isPast: boolean,
|
isPast: boolean,
|
||||||
isTransitive: boolean,
|
isTransitive: boolean,
|
||||||
isCompound: "stative" | "dynamic" | false,
|
isCompound: "stative" | "dynamic" | false,
|
||||||
blocks: RenderedVPSBlock[],
|
blocks: Block[][],
|
||||||
verb: VerbRendered,
|
kids: Kid[],
|
||||||
englishBase?: string[],
|
englishBase?: string[],
|
||||||
form: FormVersion,
|
form: FormVersion,
|
||||||
whatsAdjustable: "both" | "king" | "servant",
|
whatsAdjustable: "both" | "king" | "servant",
|
||||||
|
@ -838,7 +837,7 @@ export type EquativeRendered = EquativeSelection & {
|
||||||
|
|
||||||
export type EPRendered = {
|
export type EPRendered = {
|
||||||
type: "EPRendered",
|
type: "EPRendered",
|
||||||
blocks: Block[],
|
blocks: Block[][],
|
||||||
kids: Kid[],
|
kids: Kid[],
|
||||||
englishBase?: string[],
|
englishBase?: string[],
|
||||||
omitSubject: boolean,
|
omitSubject: boolean,
|
||||||
|
@ -866,12 +865,54 @@ export type EntryLookupPortal<X extends VerbEntry | DictionaryEntry> = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EquativeBlock = { type: "equative", equative: EquativeRendered };
|
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 =
|
export type Block =
|
||||||
| Rendered<SubjectSelectionComplete>
|
| Rendered<SubjectSelectionComplete>
|
||||||
|
| Rendered<ObjectSelectionComplete>
|
||||||
| Rendered<APSelection>
|
| Rendered<APSelection>
|
||||||
| Rendered<PredicateSelectionComplete>
|
| Rendered<PredicateSelectionComplete>
|
||||||
| { type: "nu" }
|
| PerfectParticipleBlock
|
||||||
|
| PerfectEquativeBlock
|
||||||
|
| ModalVerbBlock
|
||||||
|
| ModalVerbKedulPart
|
||||||
|
| { type: "negative", imperative: boolean }
|
||||||
|
| PerfectiveHeadBlock
|
||||||
|
| VerbRenderedBlock
|
||||||
|
| VerbComplementBlock
|
||||||
| EquativeBlock;
|
| EquativeBlock;
|
||||||
|
|
||||||
export type Kid =
|
export type Kid =
|
||||||
|
|
Loading…
Reference in New Issue