Start supporting printing unifications

This commit is contained in:
Ayaz Hafiz 2023-07-16 22:37:06 -05:00
parent 9e055dcf53
commit 87bb6c8437
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
8 changed files with 446 additions and 30 deletions

View file

@ -2,30 +2,39 @@ import React from "react";
import { AllEvents, Event, UnificationMode } from "../schema";
import { Refine } from "../utils/refine";
import clsx from "clsx";
import { Engine, EventIndex } from "../engine/engine";
import { lastSubEvent } from "../engine/event_util";
import { VariableEl } from "./Variable";
interface UiProps {
events: AllEvents;
}
export default function Ui({ events }: UiProps): JSX.Element {
const engine = new Engine(events);
return (
<div className="font-mono">
<EventList root events={events}></EventList>
<div className="font-mono mt-4">
<EventList engine={engine} root events={events}></EventList>
</div>
);
}
interface EventListProps {
engine: Engine;
events: Event[];
root?: boolean;
}
function EventList({ events, root }: EventListProps): JSX.Element {
const MT = "mt-2.5";
const UNFOCUSED = "opacity-40";
function EventList({ engine, events, root }: EventListProps): JSX.Element {
return (
<ul className={clsx(root ? "ml-2 mt-4" : "ml-[1.5em]", "relative")}>
<ul className={clsx(MT, root ? "ml-2" : "ml-[1.5em]", "relative")}>
{events.map((event, i) => (
<li key={i} className="mt-2">
<OneEvent event={event} />
<li key={i} className={MT}>
<OneEvent engine={engine} event={event} />
</li>
))}
</ul>
@ -33,13 +42,14 @@ function EventList({ events, root }: EventListProps): JSX.Element {
}
interface OneEventProps {
engine: Engine;
event: Event;
}
function OneEvent({ event }: OneEventProps): JSX.Element {
function OneEvent({ event, engine }: OneEventProps): JSX.Element {
switch (event.type) {
case "Unification":
return <Unification event={event} />;
return <Unification engine={engine} event={event} />;
case "VariableUnified":
return <></>;
case "VariableSetDescriptor":
@ -54,47 +64,76 @@ const UN_UNKNOWN = "❔";
const UN_SUCCESS = "✅";
const UN_FAILURE = "❌";
function Unification({
event,
}: {
interface UnificationProps {
engine: Engine;
event: Refine<Event, "Unification">;
}): JSX.Element {
const { left, right, mode, subevents, success } = event;
}
function Unification({ engine, event }: UnificationProps): JSX.Element {
const { mode, subevents, success } = event;
const beforeUnificationIndex = engine.getEventIndex(event);
const afterUnificationIndex = engine.getEventIndex(lastSubEvent(event));
const leftVar = (index: EventIndex) => (
<VariableEl engine={engine} index={index} variable={event.left} />
);
const rightVar = (index: EventIndex) => (
<VariableEl engine={engine} index={index} variable={event.right} />
);
const [isOpen, setIsOpen] = React.useState(false);
const result = success ? UN_SUCCESS : UN_FAILURE;
const modeIcon = <UnificationModeIcon mode={mode} />;
const dropdownIcon = isOpen ? DROPDOWN_OPEN : DROPDOWN_CLOSED;
const headLineIcon = isOpen ? UN_UNKNOWN : result;
const headLine = (
<button onClick={() => setIsOpen(!isOpen)} className="w-full text-left">
<span className="text-slate-400 mr-2">{dropdownIcon}</span>
{headLineIcon} {left} {modeIcon} {right}
</button>
const resultIcon = success ? UN_SUCCESS : UN_FAILURE;
const resultHeadline = <Headline icon={resultIcon}></Headline>;
const topHeadline = (
<Headline icon={isOpen ? UN_UNKNOWN : resultIcon}></Headline>
);
function getHeadline(index: EventIndex) {
return (
<button onClick={() => setIsOpen(!isOpen)} className="w-full text-left">
<span className="text-slate-400 mr-2">{dropdownIcon}</span>
{topHeadline} {leftVar(index)} {modeIcon} {rightVar(index)}
</button>
);
}
if (!isOpen) {
return <div className="opacity-60">{headLine}</div>;
const headLine = getHeadline(afterUnificationIndex);
return <div className={UNFOCUSED}>{headLine}</div>;
} else {
const dropdownTransparent = (
<span className="text-transparent mr-2">{dropdownIcon}</span>
);
const headlineBefore = getHeadline(beforeUnificationIndex);
const headlineAfter = (
<div className={MT}>
{dropdownTransparent}
{resultHeadline} {leftVar(afterUnificationIndex)} {modeIcon}{" "}
{rightVar(afterUnificationIndex)}
</div>
);
return (
<div>
<div>{headLine}</div>
<EventList events={subevents} />
<div className="mt-2">
{dropdownTransparent}
{result} {left} {modeIcon} {right}
</div>
<div>{headlineBefore}</div>
<EventList engine={engine} events={subevents} />
{headlineAfter}
</div>
);
}
}
function Headline({ icon }: { icon: string }): JSX.Element {
return <span className="">{icon}</span>;
}
function UnificationModeIcon({ mode }: { mode: UnificationMode }): JSX.Element {
switch (mode.type) {
case "Eq":

View file

@ -0,0 +1,92 @@
import clsx from "clsx";
import { Engine, EventIndex } from "../engine/engine";
import { TypeDescriptor } from "../engine/subs";
import { Variable } from "../schema";
import { assertExhaustive } from "../utils/exhaustive";
interface VariableProps {
engine: Engine;
index: EventIndex;
variable: Variable;
}
export function VariableEl({
engine,
index,
variable,
}: VariableProps): JSX.Element {
engine.stepTo(index);
const desc = engine.subs.get_root(variable);
const { name, bg } = contentStyles(desc);
return (
<span className={clsx("py-0 pl-0 pr-1 rounded-md", bg)}>
<span className="ring-1 ring-inset ring-black-100 mr-1 px-1 bg-white rounded-md">
{variable}
</span>
{name}
</span>
);
}
interface ContentStyles {
name: string;
bg: string;
}
function contentStyles(desc: TypeDescriptor | undefined): ContentStyles {
if (!desc) {
return { name: "???", bg: "bg-red-500" };
}
const content = desc.content;
switch (content.type) {
case "Flex":
return { name: "Flex", bg: "bg-blue-300" };
case "FlexAble":
return { name: "FlexAble", bg: "bg-blue-400" };
case "Rigid":
return { name: "Rigid", bg: "bg-indigo-300" };
case "RigidAble":
return { name: "RigidAble", bg: "bg-indigo-400" };
case "Recursive":
return { name: "Rec", bg: "bg-blue-grey-500" };
case "LambdaSet":
return { name: "LambdaSet", bg: "bg-green-500" };
case "Alias": {
switch (content.kind.type) {
case "Structural":
return { name: "Alias", bg: "bg-yellow-300" };
case "Opaque":
return { name: "Opaque", bg: "bg-amber-400" };
default:
assertExhaustive(content.kind);
}
break;
}
case "Apply":
return { name: "Apply", bg: "bg-orange-500" };
case "Function":
return { name: "Func", bg: "bg-teal-400" };
case "Record":
return { name: "Record", bg: "bg-purple-400" };
case "Tuple":
return { name: "Tuple", bg: "bg-deep-purple-400" };
case "TagUnion":
return { name: "Tags", bg: "bg-cyan-200" };
case "FunctionOrTagUnion":
return { name: "Func|Tags", bg: "bg-cyan-300" };
case "RecursiveTagUnion":
return { name: "RecTags", bg: "bg-cyan-400" };
case "RangedNumber":
return { name: "", bg: "bg-lime-400" };
case "EmptyRecord":
return { name: "{}", bg: "bg-purple-400" };
case "EmptyTuple":
return { name: "()", bg: "bg-deep-purple-400" };
case "EmptyTagUnion":
return { name: "[]", bg: "bg-cyan-200" };
case "Error":
return { name: "Error", bg: "bg-red-400" };
}
}