working - quiz is broken

This commit is contained in:
lingdocs 2022-06-04 17:32:54 -05:00
parent 4e5075ca19
commit 0f720e3be1
16 changed files with 748 additions and 181 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@lingdocs/pashto-inflector",
"version": "2.8.0",
"version": "2.8.1",
"author": "lingdocs.com",
"description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations",
"homepage": "https://verbs.lingdocs.com",

View File

@ -0,0 +1,306 @@
import * as T from "../../types";
import classNames from "classnames";
import {
getEnglishFromRendered,
} from "../../lib/phrase-building/np-tools";
function Block({ opts, block }: {
opts: T.TextOptions,
block: T.Block,
}) {
if ("equative" in block) {
return <EquativeBlock opts={opts} eq={block.equative} />;
}
if (block.type === "AP") {
const english = getEnglishFromRendered(block);
return <AP opts={opts} english={english}>{block}</AP>
}
if (block.type === "subjectSelection") {
return <SubjectBlock opts={opts} np={block.selection} />
}
if (block.type === "predicateSelection") {
const english = getEnglishFromRendered(block.selection);
return <div className="text-center">
<div>Pred</div>
{block.selection.type === "EQComp"
? <EqCompBlock opts={opts} comp={block.selection.selection} />
: <NP opts={opts} english={english}>{block.selection}</NP>}
</div>
}
if (block.type === "nu") {
return <NUBlock opts={opts} />
}
return null;
}
export default Block;
function NUBlock({ opts }: {
opts: T.TextOptions,
}) {
return <div>
<div
className={classNames("d-flex flex-row justify-content-center align-items-center")}
style={{
border: "2px solid black",
padding: "1rem",
textAlign: "center",
}}
>
nu
</div>
<div className="small text-muted text-center" style={{
// TODO: find a better way to keep this limited to the width of the div above
// don't let this make the div above expand
margin: "0 auto",
maxWidth: "300px",
}}>not</div>
</div>;
}
function EquativeBlock({ opts, eq }: {
opts: T.TextOptions,
eq: T.EquativeRendered,
}) {
return <div>
<div
className={classNames("d-flex flex-row justify-content-center align-items-center")}
style={{
border: "2px solid black",
padding: "1rem",
textAlign: "center",
}}
>
{"short" in eq.ps ? eq.ps.short[0].f : eq.ps[0].f}
</div>
</div>;
}
function SubjectBlock({ opts, np }: {
opts: T.TextOptions,
np: T.Rendered<T.NPSelection>,
}) {
const english = getEnglishFromRendered(np);
return <div className="text-center">
<div>Subject</div>
<NP opts={opts} english={english}>{np}</NP>
</div>;
}
function EqCompBlock({ opts, comp }: {
opts: T.TextOptions,
comp: T.Rendered<T.EqCompSelection["selection"]>,
}) {
function AdjectiveBlock({ opts, adj }: {
opts: T.TextOptions,
adj: T.Rendered<T.AdjectiveSelection>,
}) {
return <div>
<div
className={classNames("d-flex flex-row justify-content-center align-items-center")}
style={{
border: "2px solid black",
padding: "1rem",
textAlign: "center",
}}
>
{adj.ps[0].f}
</div>
<div>Adj.</div>
{adj.e && <div className="small text-muted text-center" style={{
// TODO: find a better way to keep this limited to the width of the div above
// don't let this make the div above expand
margin: "0 auto",
maxWidth: "300px",
}}>{adj.e}</div>}
</div>;
}
function LocAdvBlock({ opts, adv }: {
opts: T.TextOptions,
adv: T.Rendered<T.LocativeAdverbSelection>,
}) {
return <div>
<div
className={classNames("d-flex flex-row justify-content-center align-items-center")}
style={{
border: "2px solid black",
padding: "1rem",
textAlign: "center",
}}
>
{adv.ps[0].f}
</div>
<div>Loc. Adv.</div>
{adv.e && <div className="small text-muted text-center" style={{
// TODO: find a better way to keep this limited to the width of the div above
// don't let this make the div above expand
margin: "0 auto",
maxWidth: "300px",
}}>{adv.e}</div>}
</div>;
}
return <div className="text-center mb-2">
<div>Comp.</div>
{comp.type === "adjective"
? <AdjectiveBlock opts={opts} adj={comp} />
: comp.type === "loc. adv."
? <LocAdvBlock opts={opts} adv={comp} />
: <div>
<Sandwich opts={opts} sandwich={comp} />
<div>Sandwich</div>
</div>}
</div>;
}
function AP({ opts, children, english }: {
opts: T.TextOptions,
children: T.Rendered<T.APSelection>,
english?: string,
}) {
const ap = children;
if (ap.selection.type === "adverb") {
return <div>
<div
className={classNames("d-flex flex-row justify-content-center align-items-center")}
style={{
border: "2px solid black",
padding: "1rem",
textAlign: "center",
}}
>
{ap.selection.ps[0].f}
</div>
<div>AP</div>
{english && <div className="small text-muted text-center" style={{
// TODO: find a better way to keep this limited to the width of the div above
// don't let this make the div above expand
margin: "0 auto",
maxWidth: "300px",
}}>{english}</div>}
</div>;
}
return <div>
<Sandwich opts={opts} sandwich={ap.selection} />
<div>AP</div>
{english && <div className="small text-muted text-center" style={{
// TODO: find a better way to keep this limited to the width of the div above
// don't let this make the div above expand
margin: "0 auto",
maxWidth: "300px",
}}>{english}</div>}
</div>;
}
function Sandwich({ opts, sandwich }: {
opts: T.TextOptions,
sandwich: T.Rendered<T.SandwichSelection<T.Sandwich>>,
}) {
return <div>
<div className="text-center">Sandwich 🥪</div>
<div
className={classNames("d-flex flex-row justify-content-center align-items-center")}
style={{
border: "2px solid black",
padding: "0.75rem 0.5rem 0.25rem 0.5rem",
textAlign: "center",
}}
>
<div className="d-flex flex-row justify-content-between align-items-end">
<Possesors opts={opts}>{sandwich.inside.selection.type !== "pronoun" ? sandwich.inside.selection.possesor : undefined}</Possesors>
<div className="mr-2 ml-1 mb-1"><strong>{sandwich.before ? sandwich.before.f : ""}</strong></div>
<div>
<NP opts={opts} inside>{sandwich.inside}</NP>
</div>
<div className="ml-2 mr-1 mb-1"><strong>{sandwich.after ? sandwich.after.f : ""}</strong></div>
</div>
</div>
</div>;
}
function NP({ opts, children, inside, english }: {
opts: T.TextOptions,
children: T.Rendered<T.NPSelection>,
inside?: boolean,
english?: string,
}) {
const np = children;
const hasPossesor = !!(np.selection.type !== "pronoun" && np.selection.possesor);
return <div>
<div
className={classNames("d-flex flex-row justify-content-center align-items-center", { "pt-2": !inside && hasPossesor })}
style={{
border: "2px solid black",
padding: inside ? "0.3rem" : hasPossesor ? "0.5rem 1rem 0.25rem 1rem" : "1rem",
textAlign: "center",
}}
>
{!inside && <Possesors opts={opts}>{np.selection.type !== "pronoun" ? np.selection.possesor : undefined}</Possesors>}
<Adjectives opts={opts}>{np.selection.adjectives}</Adjectives>
<div> {np.selection.ps[0].f}</div>
</div>
<div className={inside ? "small" : ""}>NP</div>
{english && <div className="small text-muted text-center" style={{
// TODO: find a better way to keep this limited to the width of the div above
// don't let this make the div above expand
margin: "0 auto",
maxWidth: "300px",
}}>{english}</div>}
</div>
}
function Possesors({ opts, children }: {
opts: T.TextOptions,
children: { shrunken: boolean, np: T.Rendered<T.NPSelection> } | undefined,
}) {
if (!children) {
return null;
}
const contraction = checkForContraction(children.np);
return <div className="d-flex flex-row mr-1 align-items-end" style={{
marginBottom: "0.5rem",
borderBottom: "1px solid grey",
}}>
{children.np.selection.type !== "pronoun" && <Possesors opts={opts}>{children.np.selection.possesor}</Possesors>}
<div>
{contraction && <div className="mb-1">({contraction})</div>}
<div className={classNames("d-flex", "flex-row", "align-items-center", { "text-muted": contraction })}>
<div className="mr-1 pb-2">du</div>
<div>
<NP opts={opts} inside>{children.np}</NP>
</div>
</div>
</div>
</div>
}
function checkForContraction(np: T.Rendered<T.NPSelection>): string | undefined {
if (np.selection.type !== "pronoun") return undefined;
if (np.selection.person === T.Person.FirstSingMale || np.selection.person === T.Person.FirstSingFemale) {
return "zmaa"
}
if (np.selection.person === T.Person.SecondSingMale || np.selection.person === T.Person.SecondSingFemale) {
return "staa"
}
if (np.selection.person === T.Person.FirstPlurMale || np.selection.person === T.Person.FirstPlurFemale) {
return "zmoonG"
}
if (np.selection.person === T.Person.SecondPlurMale || np.selection.person === T.Person.SecondPlurFemale) {
return "staaso"
}
return undefined;
}
function Adjectives({ opts, children }: {
opts: T.TextOptions,
children: T.Rendered<T.AdjectiveSelection>[] | undefined,
}) {
if (!children) {
return null;
}
return <em className="mr-1">
{children.map(a => a.ps[0].f).join(" ")}{` `}
</em>
}

View File

@ -0,0 +1,18 @@
import * as T from "../../types";
import Block from "../blocks/Block";
function EPBlocksDisplay({ opts, rendered }: { opts: T.TextOptions, rendered: T.EPRendered }) {
const blocks = rendered.omitSubject
? rendered.blocks.filter(b => b.type !== "subjectSelection")
: rendered.blocks;
return <div className="d-flex flex-row justify-content-center align-items-end mt-3">
{blocks.map((block, i) => (
<div key={Math.random()} className="mr-2">
<Block opts={opts} block={block} />
</div>
))}
</div>;
}
export default EPBlocksDisplay;

View File

@ -1,17 +1,22 @@
import * as T from "../../types";
import { completeEPSelection, renderEP } from "../../lib/phrase-building/render-ep";
import { compileEP } from "../../lib/phrase-building/compile";
import Examples from "../Examples";
import ButtonSelect from "../ButtonSelect";
import { getRenderedSubjectSelection, getSubjectSelection } from "../../lib/phrase-building/blocks-utils";
import { getPredicateSelectionFromBlocks, getSubjectSelection, getSubjectSelectionFromBlocks } from "../../lib/phrase-building/blocks-utils";
import { useState } from "react";
import EPTextDisplay from "./EPTextDisplay";
import EPBlocksDisplay from "./EPBlocksDisplay";
type Mode = "text" | "blocks";
function EPDisplay({ eps, opts, setOmitSubject }: {
eps: T.EPSelectionState,
opts: T.TextOptions,
setOmitSubject: (value: "true" | "false") => void,
}) {
const [mode] = useState<Mode>("text");
const EP = completeEPSelection(eps);
const subject = getSubjectSelection(eps.blocks);
if (!EP) {
return <div className="lead text-center my-4">
{(!subject && !eps.predicate[eps.predicate.type])
@ -25,9 +30,11 @@ function EPDisplay({ eps, opts, setOmitSubject }: {
}
const rendered = renderEP(EP);
const result = compileEP(rendered);
const renderedSubject = getRenderedSubjectSelection(rendered.blocks).selection;
const renderedSubject = getSubjectSelectionFromBlocks(rendered.blocks).selection;
const renderedPredicate = getPredicateSelectionFromBlocks(rendered.blocks).selection;
return <div className="text-center pt-3">
<div className="mb-2">
<div className="mb-2 d-flex flex-row justify-content-between align-items-center">
{/* <ModeSelect value={mode} onChange={setMode} /> */}
<ButtonSelect
small
value={(eps.omitSubject ? "true" : "false") as "true" | "false"}
@ -37,31 +44,28 @@ function EPDisplay({ eps, opts, setOmitSubject }: {
]}
handleChange={setOmitSubject}
/>
<div />
</div>
{"long" in result.ps ?
<div>
<VariationLayer vs={result.ps.long} opts={opts} />
<VariationLayer vs={result.ps.short} opts={opts} />
{result.ps.mini && <VariationLayer vs={result.ps.mini} opts={opts} />}
</div>
: <VariationLayer vs={result.ps} opts={opts} />
}
{result.e && <div className="text-muted mt-3">
{result.e.map((e, i) => <div key={i}>{e}</div>)}
</div>}
{mode === "text"
? <EPTextDisplay opts={opts} compiled={result} />
: <EPBlocksDisplay opts={opts} rendered={rendered} />}
{EP.predicate.selection.selection.type === "participle" && <div style={{ maxWidth: "6 00px", margin: "0 auto" }} className="alert alert-warning mt-3 pt-4">
<p> NOTE: This means that the subject {renderedSubject.selection.e ? `(${renderedSubject.selection.e})` : ""} is <strong>the action/idea</strong> of
{` `}
"{rendered.predicate.selection.e ? rendered.predicate.selection.e : "the particple"}".</p>
"{renderedPredicate.selection.e ? renderedPredicate.selection.e : "the particple"}".</p>
<p>It <strong>does not</strong> mean that the subject is doing the action, which is what the transaltion sounds like in English.</p>
</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>;
}
// 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;

View File

@ -38,7 +38,7 @@ function EPExplorer(props: {
entryFeeder: T.EntryFeeder,
}) {
const [mode, setMode] = useStickyState<"charts" | "phrases">("charts", "EPExplorerMode");
const [eps, adjustEps] = useStickyReducer(epsReducer, blankEps, "EPState5", flashMessage);
const [eps, adjustEps] = useStickyReducer(epsReducer, blankEps, "EPState6", flashMessage);
const [alert, setAlert] = useState<string | undefined>(undefined);
const [showClipped, setShowClipped] = useState<string>("");
const parent = useRef<HTMLDivElement>(null);

View File

@ -0,0 +1,28 @@
import * as T from "../../types";
import Examples from "../Examples";
function EPTextDisplay({ compiled, opts }: { compiled: {
ps: T.SingleOrLengthOpts<T.PsString[]>;
e?: string[] | undefined;
}, opts: T.TextOptions }) {
function VariationLayer({ vs }: { vs: T.PsString[] }) {
return <div className="mb-2">
<Examples opts={opts} lineHeight={0}>{vs}</Examples>
</div>;
}
return <div>
{"long" in compiled.ps ?
<div>
<VariationLayer vs={compiled.ps.long} />
<VariationLayer vs={compiled.ps.short} />
{compiled.ps.mini && <VariationLayer vs={compiled.ps.mini} />}
</div>
: <VariationLayer vs={compiled.ps} />
}
{compiled.e && <div className="text-muted mt-3">
{compiled.e.map((e, i) => <div key={i}>{e}</div>)}
</div>}
</div>;
}
export default EPTextDisplay;

View File

@ -4,7 +4,7 @@ import {
personNumber,
} from "../../lib/misc-helpers";
import { isUnisexNounEntry } from "../../lib/type-predicates";
import { checkForMiniPronounsError } from "../../lib/phrase-building/compile";
import { checkEPForMiniPronounsError } from "../../lib/phrase-building/compile";
import { adjustSubjectSelection, getSubjectSelection, insertNewAP, removeAP, setAP, shiftBlock } from "../../lib/phrase-building/blocks-utils";
type EpsReducerAction = {
@ -189,7 +189,7 @@ export default function epsReducer(eps: T.EPSelectionState, action: EpsReducerAc
}
function ensureMiniPronounsOk(old: T.EPSelectionState, eps: T.EPSelectionState, sendAlert?: (msg: string) => void): T.EPSelectionState {
const error = checkForMiniPronounsError(eps);
const error = checkEPForMiniPronounsError(eps);
if (error) {
if (sendAlert) sendAlert(error);
return old;

View File

@ -106,6 +106,12 @@ function VPExplorerQuiz(props: {
setAnswerBlank("");
setQuizState(tickQuizState(quizState.vps));
}
if (1 < 3) {
return <div className="text-center my-4">
<h3>The quiz is broken 😭</h3>
<p>It will be fixed in like a week or two... hopefully.</p>
</div>
}
return <div className="mt-4">
<ProgressBar quizState={quizState} />
<div className="d-flex flex-row justify-content-around flex-wrap">
@ -322,6 +328,7 @@ function tickQuizState(startingWith: T.VPSelectionComplete | QuizState): QuizSta
}
const answer = makeRes(newVps);
const wrongAnswers = wrongVpsS.map(makeRes);
console.log({ newVps, answer, wrongAnswers });
const allAnswers = shuffleArray([...wrongAnswers, answer]);
const options = allAnswers.map(getOptionFromResult);
return {

View File

@ -108,6 +108,20 @@ function accentSyllable(s: string): string {
});
}
export function removeAccentsWLength(s: T.SingleOrLengthOpts<T.PsString[]>): T.SingleOrLengthOpts<T.PsString[]> {
if ("long" in s) {
return {
long: removeAccentsWLength(s.long) as T.PsString[],
short: removeAccentsWLength(s.short) as T.PsString[],
...s.mini ? {
mini: removeAccentsWLength(s.mini) as T.PsString[],
} : {},
};
}
return removeAccents(s);
}
export function removeAccents(s: T.PsString): T.PsString;
export function removeAccents(s: string): string;
export function removeAccents(s: T.PsString[]): T.PsString[];

View File

@ -28,6 +28,12 @@ export function pickPersInf<T>(s: T.OptionalPersonInflections<T>, persInf: T.Per
return s;
}
export function getFirstSecThird(p: T.Person): 1 | 2 | 3 {
if ([0, 1, 6, 7].includes(p)) return 1;
if ([2, 3, 8, 9].includes(p)) return 2;
return 3;
}
// export function pickPersInf(
// s: T.OptionalPersonInflections<T.LengthOptions<T.PsString>>,
// persInf: T.PersonInflectionsField,

View File

@ -984,6 +984,10 @@ export function psStringFromEntry(entry: T.PsString): T.PsString {
};
}
export function getLength<U>(x: T.SingleOrLengthOpts<U>, length: "long" | "short"): U {
return ("long" in x) ? x[length] : x;
}
export function getLong<U>(x: T.SingleOrLengthOpts<U>): U {
if ("long" in x) {
return x.long;
@ -991,6 +995,13 @@ export function getLong<U>(x: T.SingleOrLengthOpts<U>): U {
return x;
}
export function getShort<U>(a: T.SingleOrLengthOpts<U>): U {
if ("long" in a) {
return a.short;
}
return a;
}
export function capitalizeFirstLetter(string: string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}

View File

@ -1,4 +1,5 @@
import * as T from "../../types";
import { getLength } from "../p-text-helpers";
export function getSubjectSelection(blocks: T.EPSBlockComplete[] | T.VPSBlockComplete[]): T.SubjectSelectionComplete;
export function getSubjectSelection(blocks: T.EPSBlock[] | T.VPSBlock[]): T.SubjectSelection;
@ -10,6 +11,26 @@ export function getSubjectSelection(blocks: T.EPSBlock[] | T.EPSBlockComplete[]
return b.block;
}
export function getSubjectSelectionFromBlocks(blocks: T.Block[]): T.Rendered<T.SubjectSelectionComplete> {
const b = blocks.find(f => f.type === "subjectSelection");
if (!b || b.type !== "subjectSelection") {
throw new Error("subjectSelection not found in blocks");
}
return b;
}
export function getPredicateSelectionFromBlocks(blocks: T.Block[]): T.Rendered<T.PredicateSelectionComplete> {
const b = blocks.find(f => f.type === "predicateSelection");
if (!b || b.type !== "predicateSelection") {
throw new Error("predicateSelection not found in blocks");
}
return b;
}
export function getAPsFromBlocks(blocks: T.Block[]): T.Rendered<T.APSelection>[] {
return blocks.filter(b => b.type === "AP") as T.Rendered<T.APSelection>[];
}
export function getObjectSelection(blocks: T.VPSBlockComplete[]): T.ObjectSelectionComplete;
export function getObjectSelection(blocks: T.VPSBlock[]): T.ObjectSelection;
export function getObjectSelection(blocks: T.VPSBlock[] | T.VPSBlockComplete[]): T.ObjectSelection | T.ObjectSelectionComplete {
@ -185,6 +206,29 @@ export function isNoObject(b: T.VPSBlock["block"] | T.EPSBlock["block"]): b is {
return !!(b && b.type === "objectSelection" && b.selection === "none");
}
export function specifyEquativeLength(blocks: T.Block[], length: "long" | "short"): T.Block[] {
const i = blocks.findIndex(b => b.type === "equative");
if (i === -1) throw new Error("equative block not found in EPRendered");
const eq = blocks[i];
if (eq.type !== "equative") throw new Error("error searching for equative block");
const adjusted = [...blocks];
adjusted[i] = {
...eq,
equative: {
...eq.equative,
ps: getLength(eq.equative.ps, length),
},
};
return adjusted;
}
export function hasEquativeWithLengths(blocks: T.Block[]): boolean {
const equative = blocks.find(x => x.type === "equative");
if (!equative) throw new Error("equative not found in blocks");
if (equative.type !== "equative") throw new Error("error finding equative in blocks");
return "long" in equative.equative.ps;
}
function arrayMove<X>(ar: X[], old_index: number, new_index: number): X[] {
const arr = [...ar];
const new_i = (new_index >= arr.length)

View File

@ -1,6 +1,6 @@
import * as T from "../../types";
import {
concatPsString,
concatPsString, getLong,
} from "../p-text-helpers";
import {
Segment,
@ -8,7 +8,7 @@ import {
flattenLengths,
combineSegments,
splitOffLeapfrogWord,
putKidsInKidsSection,
putKidsInKidsSection as oldPutKidsInKidsSection,
} from "./segment";
import {
removeAccents,
@ -25,15 +25,17 @@ import { pronouns } from "../grammar-units";
import { completeEPSelection, renderEP } from "./render-ep";
import { completeVPSelection } from "./vp-tools";
import { renderVP } from "./render-vp";
import { getRenderedObjectSelection, getRenderedSubjectSelection } from "./blocks-utils";
import { getAPsFromBlocks, getPredicateSelectionFromBlocks, getRenderedObjectSelection, getRenderedSubjectSelection, getSubjectSelectionFromBlocks, hasEquativeWithLengths, specifyEquativeLength } from "./blocks-utils";
const blank: T.PsString = {
p: "______",
f: "______",
};
// TODO: GET BLANKING WORKING!
// const blank: T.PsString = {
// p: "______",
// f: "______",
// };
type BlankoutOptions = { equative?: boolean, ba?: boolean, kidsSection?: boolean };
const kidsBlank = makeSegment({ p: "___", f: "___" }, ["isKid"]);
// const kidsBlank = makeSegment({ p: "___", f: "___" }, ["isKid"]);
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 [] };
@ -80,7 +82,7 @@ function compilePs({ blocks, kids, verb: { head, rest }, VP }: CompilePsInput):
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 = putKidsInKidsSection([...blocks, ...verbSegments], kids);
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
@ -145,37 +147,6 @@ export function getVPSegmentsAndKids(VP: T.VPRendered, form?: T.FormVersion): {
};
}
export function getEPSegmentsAndKids(EP: T.EPRendered, blankOut?: BlankoutOptions): { kids: Segment[], blocks: Segment[] } {
const possToShrink = findPossesivesToShrinkInEP(EP);
const blocks = EP.blocks.reduce((accum, block) => {
if (block.type === "subjectSelection") {
if (EP.omitSubject) return accum;
return [
...accum,
makeSegment(getPashtoFromRendered(block.selection, false)),
];
}
return [
...accum,
makeSegment(getPashtoFromRendered(block, false)),
];
}, [] as Segment[]);
const predicate = makeSegment(getPashtoFromRendered(EP.predicate, false));
return {
kids: blankOut?.kidsSection ? [kidsBlank] : orderKidsSection([
...EP.equative.hasBa
? (
blankOut?.ba ? [kidsBlank] : [makeSegment(grammarUnits.baParticle, ["isBa", "isKid"])])
: [],
...possToShrink.map(a => shrinkNP(a.np)),
]),
blocks: [
...blocks,
predicate
],
};
}
function arrangeVerbWNegative(head: T.PsString | undefined, restRaw: T.PsString[], V: T.VerbRendered): Segment[][] {
const hasLeapfrog = isPerfectTense(V.tense) || isModalTense(V.tense);
const rest = (() => {
@ -292,42 +263,67 @@ function arrangeVerbWNegative(head: T.PsString | undefined, restRaw: T.PsString[
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?: boolean, blankOut?: BlankoutOptions): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string[] } {
const { kids, blocks } = getEPSegmentsAndKids(EP, blankOut);
const equative = EP.equative.ps;
const psResult = compileEPPs({
blocks,
kids,
equative: blankOut?.equative ? [blank] : equative,
negative: EP.equative.negative,
});
const psResult = compileEPPs(EP.blocks, EP.kids, blankOut);
return {
ps: combineLengths ? flattenLengths(psResult) : psResult,
e: compileEnglishEP(EP),
};
}
function compileEPPs({ blocks, kids, equative, negative }: {
blocks: Segment[],
kids: Segment[],
equative: T.SingleOrLengthOpts<T.PsString[]>,
negative: boolean,
}): T.SingleOrLengthOpts<T.PsString[]> {
if ("long" in equative) {
function compileEPPs(blocks: T.Block[], kids: T.Kid[], blankOut?: BlankoutOptions): T.SingleOrLengthOpts<T.PsString[]> {
if (hasEquativeWithLengths(blocks)) {
return {
long: compileEPPs({ blocks, kids, equative: equative.long, negative }) as T.PsString[],
short: compileEPPs({ blocks, kids, equative: equative.short, negative }) as T.PsString[],
long: compileEPPs(specifyEquativeLength(blocks, "long"), kids, blankOut) as T.PsString[],
short: compileEPPs(specifyEquativeLength(blocks, "short"), kids, blankOut) as T.PsString[],
};
}
const allSegments = putKidsInKidsSection([
...blocks,
...negative ? [
makeSegment({ p: "نه", f: "nú" }),
makeSegment(removeAccents(equative))
] : [
makeSegment(equative),
],
], kids);
return removeDuplicates(combineSegments(allSegments, "spaces"));
const subjectPerson = getSubjectSelectionFromBlocks(blocks)
.selection.selection.person;
const blocksWKids = putKidsInKidsSection(blocks, kids);
return removeDuplicates(combineIntoText(blocksWKids, subjectPerson));
}
function combineIntoText(pieces: (T.Block | T.Kid)[], subjectPerson: T.Person): T.PsString[] {
const first = pieces[0];
const rest = pieces.slice(1);
const firstPs = getPsFromPiece(first, subjectPerson);
if (!rest.length) {
return firstPs;
}
return combineIntoText(rest, subjectPerson).flatMap(r => (
firstPs.map(fPs => concatPsString(fPs, " ", r))
)
);
}
function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsString[] {
if (piece.type === "ba") {
return [grammarUnits.baParticle];
}
if (piece.type === "mini-pronoun") {
return [piece.ps];
}
if (piece.type === "nu") {
return [{ p: "نه", f: "nú" }];
}
if (piece.type === "equative") {
// length will already be specified in compileEPPs - this is just for type safety
return getLong(piece.equative.ps);
}
if (piece.type === "subjectSelection" || piece.type === "predicateSelection") {
return getPashtoFromRendered(piece.selection, subjectPerson);
}
// if (piece.type === "AP") {
return getPashtoFromRendered(piece, subjectPerson);
// }
}
function getEngAPs(blocks: T.Block[]): string {
return getAPsFromBlocks(blocks).reduce((accum, curr) => {
const e = getEnglishFromRendered(curr);
if (!e) return accum;
return `${accum} ${e}`;
}, "");
}
function getEnglishAPs(blocks: (T.Rendered<T.SubjectSelectionComplete> | T.Rendered<T.APSelection> | T.RenderedVPSBlock)[]) {
@ -339,6 +335,16 @@ function getEnglishAPs(blocks: (T.Rendered<T.SubjectSelectionComplete> | T.Rende
}, "");
}
function putKidsInKidsSection(blocks: T.Block[], kids: T.Kid[]): (T.Block | T.Kid)[] {
const first = blocks[0];
const rest = blocks.slice(1);
return [
first,
...kids,
...rest,
];
}
function mergeSegments(s1: Segment, s2: Segment, noSpace?: "no space"): Segment {
if (noSpace) {
@ -405,10 +411,11 @@ function compileEnglishEP(EP: T.EPRendered): string[] | undefined {
function insertEWords(e: string, { subject, predicate, APs }: { subject: string, predicate: string, APs: string }): string {
return e.replace("$SUBJ", subject).replace("$PRED", predicate || "") + APs;
}
const engSubjR = getRenderedSubjectSelection(EP.blocks).selection;
const engSubjR = getSubjectSelectionFromBlocks(EP.blocks).selection;
const engPredR = getPredicateSelectionFromBlocks(EP.blocks).selection;
const engSubj = getEnglishFromRendered(engSubjR);
const engPred = getEnglishFromRendered(EP.predicate);
const engAPs = getEnglishAPs(EP.blocks);
const engPred = getEnglishFromRendered(engPredR);
const engAPs = getEngAPs(EP.blocks);
// require all English parts for making the English phrase
const b = (EP.englishBase && engSubj && engPred)
? EP.englishBase.map(e => insertEWords(e, {
@ -440,7 +447,33 @@ export function orderKidsSection(kids: Segment[]): Segment[] {
});
}
export function checkForMiniPronounsError(s: T.EPSelectionState | T.VPSelectionState): undefined | string {
export function checkEPForMiniPronounsError(s: T.EPSelectionState): undefined | string {
function findDuplicateMiniP(mp: T.MiniPronoun[]): T.MiniPronoun | undefined {
const duplicates = mp.filter((item, index) => (
mp.findIndex(m => item.ps.p === m.ps.p) !== index
));
if (duplicates.length === 0) return undefined;
return duplicates[0];
}
const kids = (() => {
const EPS = completeEPSelection(s);
if (!EPS) return undefined;
const { kids } = renderEP(EPS);
return kids;
})();
if (!kids) return undefined;
const miniPronouns = kids.filter(x => x.type === "mini-pronoun") as T.MiniPronoun[];
if (miniPronouns.length > 2) {
return "can't add another mini-pronoun, there are alread two";
}
const duplicateMiniPronoun = findDuplicateMiniP(miniPronouns);
if (duplicateMiniPronoun) {
return `there's already a ${duplicateMiniPronoun.ps.p} - ${duplicateMiniPronoun.ps.f} mini-pronoun in use, can't have two of those`;
}
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
@ -449,12 +482,6 @@ export function checkForMiniPronounsError(s: T.EPSelectionState | T.VPSelectionS
return duplicates[0];
}
const kids = (() => {
if ("predicate" in s) {
const EPS = completeEPSelection(s);
if (!EPS) return undefined;
const { kids } = getEPSegmentsAndKids(renderEP(EPS));
return kids;
}
const VPS = completeVPSelection(s);
if (!VPS) return undefined;
const { kids } = getVPSegmentsAndKids(renderVP(VPS));
@ -536,44 +563,6 @@ function findPossesivesInAdjective(a: T.Rendered<T.AdjectiveSelection>): T.Rende
return findPossesivesInNP(a.sandwich.inside);
}
type FoundNP = {
np: T.Rendered<T.NPSelection>,
from: "subject" | "predicate" | "AP",
};
export function findPossesivesToShrinkInEP(EP: T.EPRendered): FoundNP[] {
const inBlocks: FoundNP[] = EP.blocks.reduce((accum, block): FoundNP[] => {
if (block.type === "subjectSelection") {
if (EP.omitSubject) return accum;
const found: FoundNP[] = findPossesivesInNP(block.selection).map(np => ({ np, from: "subject" }));
return [
...accum,
...found,
];
}
if (block.selection.type === "sandwich") {
const found: FoundNP[] = findPossesivesInNP(block.selection.inside).map(np => ({ np, from: "AP" }));
return [
...accum,
...found,
];
}
return accum;
}, [] as FoundNP[]);
const inPredicate: FoundNP[] = ((EP.predicate.selection.type === "loc. adv.")
? []
: (EP.predicate.selection.type === "adjective")
? findPossesivesInAdjective(EP.predicate.selection)
: EP.predicate.selection.type === "sandwich"
? findPossesivesInNP(EP.predicate.selection.inside)
: EP.predicate.selection.type === "pronoun"
? []
: findPossesivesInNP({ type: "NP", selection: EP.predicate.selection })).map(np => ({ np, from: "predicate" }));
return [
...inBlocks,
...inPredicate,
];
}
// export function findPossesiveToShrinkInVP(VP: T.VPRendered): T.Rendered<T.NPSelection>[] {
// const obj: T.Rendered<T.NPSelection> | undefined = ("object" in VP && typeof VP.object === "object")

View File

@ -4,7 +4,7 @@ import {
getPersonFromNP,
} from "./vp-tools";
import { renderNPSelection } from "./render-np";
import { getPersonFromVerbForm } from "../../lib/misc-helpers";
import { getFirstSecThird, getPersonFromVerbForm } from "../../lib/misc-helpers";
import { getVerbBlockPosFromPerson } from "../misc-helpers";
import { getEnglishWord } from "../get-english-word";
import { psStringFromEntry } from "../p-text-helpers";
@ -12,39 +12,143 @@ import { isLocativeAdverbEntry } from "../type-predicates";
import { renderAdjectiveSelection } from "./render-adj";
import { renderSandwich } from "./render-sandwich";
import { EPSBlocksAreComplete, getSubjectSelection } from "./blocks-utils";
import { removeAccentsWLength } from "../accent-helpers";
import { pronouns } from "../grammar-units";
export function renderEP(EP: T.EPSelectionComplete): T.EPRendered {
const subject = getSubjectSelection(EP.blocks).selection;
const king = (subject.selection.type === "pronoun")
? "subject"
: EP.predicate.type === "NP"
? "predicate"
: "subject";
// TODO: less repetative logic
const kingPerson = king === "subject"
? getPersonFromNP(subject)
: EP.predicate.type === "NP"
? getPersonFromNP(EP.predicate.selection)
: getPersonFromNP(subject);
const kingIsParticiple = king === "subject"
? (subject.selection.type === "participle")
: (EP.predicate.type === "NP" && EP.predicate.selection.selection.type === "participle");
const { kids, blocks, englishEquativePerson } = getEPSBlocksAndKids(EP);
return {
type: "EPRendered",
king: EP.predicate.type === "Complement" ? "subject" : "predicate",
blocks: renderEPSBlocks(EP.blocks, king),
predicate: EP.predicate.type === "NP"
? renderNPSelection(EP.predicate.selection, false, true, "subject", "king")
: renderEqCompSelection(EP.predicate.selection, kingPerson),
equative: renderEquative(EP.equative, kingPerson),
blocks,
kids,
englishBase: equativeBuilders[EP.equative.tense](
kingIsParticiple ? T.Person.ThirdSingMale : kingPerson,
englishEquativePerson,
EP.equative.negative,
),
omitSubject: EP.omitSubject,
};
}
function getEPSBlocksAndKids(EP: T.EPSelectionComplete): { kids: T.Kid[], blocks: T.Block[], englishEquativePerson: T.Person } {
const subject = getSubjectSelection(EP.blocks).selection;
const commandingNP: T.NPSelection = subject.selection.type === "pronoun"
? subject
: EP.predicate.selection.type === "NP"
? EP.predicate.selection
: subject;
const commandingPerson = getPersonFromNP(commandingNP);
const equative: T.EquativeBlock = { type: "equative", equative: renderEquative(EP.equative, commandingPerson) };
const blocks: T.Block[] = [
...renderEPSBlocks(EP.omitSubject ? EP.blocks.filter(b => b.block.type !== "subjectSelection") : EP.blocks),
{
type: "predicateSelection",
selection: EP.predicate.selection.type === "NP"
? renderNPSelection(EP.predicate.selection, false, false, "subject", "king")
: renderEqCompSelection(EP.predicate.selection, commandingPerson),
},
...EP.equative.negative ? [{ type: "nu" } as T.Block] : [],
EP.equative.negative ? removeAccontsFromEq(equative) : equative,
];
const miniPronouns = findPossesivesToShrink([...EP.blocks, EP.predicate], EP.omitSubject);
const kids: T.Kid[] = orderKids([
...equative.equative.hasBa ? [{ type: "ba" } as T.Kid] : [],
...miniPronouns,
]);
return {
blocks,
kids,
englishEquativePerson: commandingNP.selection.type === "participle" ? T.Person.ThirdSingMale : commandingPerson,
};
}
function orderKids(kids: T.Kid[]): T.Kid[] {
const sorted = [...kids].sort((a, b) => {
// ba first
if (a.type === "ba") return -1;
// kinds lined up 1st 2nd 3rd person
if (a.type === "mini-pronoun" && b.type === "mini-pronoun") {
const aPers = getFirstSecThird(a.person);
const bPers = getFirstSecThird(b.person);
if (aPers < bPers) {
return -1;
}
if (aPers > bPers) {
return 1;
}
// TODO: is this enough?
return 0;
}
return 0;
});
return sorted;
}
function findPossesivesToShrink(blocks: (T.EPSBlockComplete | T.SubjectSelectionComplete | T.PredicateSelectionComplete | T.APSelection)[], omitSubject: boolean): T.MiniPronoun[] {
return blocks.reduce((kids, item) => {
const block = "block" in item ? item.block : item;
if (block.type === "subjectSelection") {
if (omitSubject) return kids;
return [
...kids,
...findShrunkenPossInNP(block.selection),
];
}
if (block.type === "AP") {
if (block.selection.type === "adverb") return kids;
return [
...kids,
...findShrunkenPossInNP(block.selection.inside),
];
}
if (block.type === "predicateSelection") {
if (block.selection.type === "EQComp") {
if (block.selection.selection.type === "sandwich") {
return [
...kids,
...findShrunkenPossInNP(block.selection.selection.inside),
];
}
return kids;
}
return [
...kids,
...findShrunkenPossInNP(block.selection),
];
}
return kids;
}, [] as T.MiniPronoun[]);
}
function findShrunkenPossInNP(NP: T.NPSelection): T.MiniPronoun[] {
if (NP.selection.type === "pronoun") return [];
if (!NP.selection.possesor) return [];
if (NP.selection.type === "noun") {
if (NP.selection.adjectives) {
const { adjectives, ...rest } = NP.selection;
return [
// TODO: ability to find possesives shrinkage in sandwiches in adjectives
// ...findShrunkenPossInAdjectives(adjectives),
...findShrunkenPossInNP({ type: "NP", selection: {
...rest,
adjectives: [],
}}),
];
}
}
if (NP.selection.possesor.shrunken) {
const person = getPersonFromNP(NP.selection.possesor.np);
const miniP: T.MiniPronoun = {
type: "mini-pronoun",
person,
ps: getMiniPronounPs(person),
source: "possesive",
np: NP.selection.possesor.np,
};
return [miniP];
}
return findShrunkenPossInNP(NP.selection.possesor.np);
}
export function getEquativeForm(tense: T.EquativeTense): { hasBa: boolean, form: T.SingleOrLengthOpts<T.VerbBlock> } {
const hasBa = (tense === "future" || tense === "wouldBe");
const baseTense = (tense === "future")
@ -58,7 +162,7 @@ export function getEquativeForm(tense: T.EquativeTense): { hasBa: boolean, form:
}
}
function renderEPSBlocks(blocks: T.EPSBlockComplete[], king: "subject" | "predicate"): (T.Rendered<T.SubjectSelectionComplete> | T.Rendered<T.APSelection>)[] {
function renderEPSBlocks(blocks: T.EPSBlockComplete[]): T.Block[] {
return blocks.map(({ block }): (T.Rendered<T.SubjectSelectionComplete> | T.Rendered<T.APSelection>) => {
if (block.type === "AP") {
if (block.selection.type === "adverb") {
@ -76,7 +180,7 @@ function renderEPSBlocks(blocks: T.EPSBlockComplete[], king: "subject" | "predic
}
return {
type: "subjectSelection",
selection: renderNPSelection(block.selection, false, false, "subject", king === "subject" ? "king" : "none")
selection: renderNPSelection(block.selection, false, false, "subject", "none")
};
});
}
@ -218,7 +322,7 @@ export function completeEPSelection(eps: T.EPSelectionState): T.EPSelectionCompl
...eps,
blocks: eps.blocks,
predicate: {
type: "Complement",
type: "predicateSelection",
selection,
},
};
@ -230,8 +334,23 @@ export function completeEPSelection(eps: T.EPSelectionState): T.EPSelectionCompl
...eps,
blocks: eps.blocks,
predicate: {
type: "NP",
type: "predicateSelection",
selection,
},
};
}
export function getMiniPronounPs(person: T.Person): T.PsString {
const [row, col] = getVerbBlockPosFromPerson(person);
return pronouns.mini[row][col][0];
}
function removeAccontsFromEq(equ: T.EquativeBlock): T.EquativeBlock {
return {
...equ,
equative: {
...equ.equative,
ps: removeAccentsWLength(equ.equative.ps),
},
};
}

View File

@ -5,8 +5,6 @@ import {
import {
concatPsString,
} from "../p-text-helpers";
// SEGMENT TOOLS
// TODO: PULL OUT FOR SHARING ACROSS COMPILE EP ETC?
type SegmentDescriptions = {
isVerbHead?: boolean,

View File

@ -552,6 +552,11 @@ export type SubjectSelectionComplete = {
selection: NPSelection,
};
export type PredicateSelectionComplete = {
type: "predicateSelection",
selection: EqCompSelection | NPSelection,
};
export type ObjectSelectionComplete = {
type: "objectSelection",
selection: NPSelection | ObjectNP,
@ -695,6 +700,7 @@ export type Rendered<
| EqCompSelection["selection"]
| SubjectSelectionComplete
| ObjectSelectionComplete
| PredicateSelectionComplete
| AdjectiveSelection
| SandwichSelection<Sandwich>
> =
@ -744,7 +750,11 @@ export type Rendered<
type: "objectSelection",
selection: Rendered<NPSelection> | Person.ThirdPlurMale | "none",
}
: ReplaceKey<
: T extends PredicateSelectionComplete
? {
type: "predicateSelection",
selection: Rendered<EqCompSelection> | Rendered<NPSelection>,
} : ReplaceKey<
Omit<T, "changeGender" | "changeNumber" | "changeDistance" | "adjectives" | "possesor">,
"e",
string
@ -794,13 +804,7 @@ export type VPSBlockComplete = {
export type EPSelectionComplete = Omit<EPSelectionState, "predicate" | "blocks"> & {
blocks: EPSBlockComplete[],
predicate: {
type: "NP",
selection: NPSelection,
} | {
type: "Complement",
selection: EqCompSelection,
},
predicate: PredicateSelectionComplete,
omitSubject: boolean,
};
@ -834,10 +838,8 @@ export type EquativeRendered = EquativeSelection & {
export type EPRendered = {
type: "EPRendered",
king: "subject" | "predicate",
blocks: (Rendered<SubjectSelectionComplete> | Rendered<APSelection>)[],
predicate: Rendered<NPSelection> | Rendered<EqCompSelection>,
equative: EquativeRendered,
blocks: Block[],
kids: Kid[],
englishBase?: string[],
omitSubject: boolean,
}
@ -863,3 +865,24 @@ export type EntryLookupPortal<X extends VerbEntry | DictionaryEntry> = {
getByTs: (ts: number) => (X | undefined),
}
export type EquativeBlock = { type: "equative", equative: EquativeRendered };
export type Block =
| Rendered<SubjectSelectionComplete>
| Rendered<APSelection>
| Rendered<PredicateSelectionComplete>
| { type: "nu" }
| EquativeBlock;
export type Kid =
| { type: "ba" }
| MiniPronoun;
export type MiniPronoun = {
type: "mini-pronoun",
person: Person,
ps: PsString,
source: "servant" | "possesive",
np: NPSelection,
};