nicer looking
This commit is contained in:
parent
8abbac4bc9
commit
e850952738
14 changed files with 547 additions and 104 deletions
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
|
|
@ -10,7 +10,7 @@ importers:
|
||||||
dependencies:
|
dependencies:
|
||||||
dope2:
|
dope2:
|
||||||
specifier: git+https://deemz.org/git/joeri/dope2.git
|
specifier: git+https://deemz.org/git/joeri/dope2.git
|
||||||
version: git+https://deemz.org/git/joeri/dope2.git#d75bf9f0f200769a5248ace8e4e2ace04fd60381
|
version: git+https://deemz.org/git/joeri/dope2.git#0096bb5559224b4c9bbe74317e07dc71cfc09c70
|
||||||
react:
|
react:
|
||||||
specifier: ^19.1.0
|
specifier: ^19.1.0
|
||||||
version: 19.1.0
|
version: 19.1.0
|
||||||
|
|
@ -633,8 +633,8 @@ packages:
|
||||||
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
|
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
dope2@git+https://deemz.org/git/joeri/dope2.git#d75bf9f0f200769a5248ace8e4e2ace04fd60381:
|
dope2@git+https://deemz.org/git/joeri/dope2.git#0096bb5559224b4c9bbe74317e07dc71cfc09c70:
|
||||||
resolution: {commit: d75bf9f0f200769a5248ace8e4e2ace04fd60381, repo: https://deemz.org/git/joeri/dope2.git, type: git}
|
resolution: {commit: 0096bb5559224b4c9bbe74317e07dc71cfc09c70, repo: https://deemz.org/git/joeri/dope2.git, type: git}
|
||||||
version: 0.0.1
|
version: 0.0.1
|
||||||
|
|
||||||
dunder-proto@1.0.1:
|
dunder-proto@1.0.1:
|
||||||
|
|
@ -1762,7 +1762,7 @@ snapshots:
|
||||||
|
|
||||||
depd@2.0.0: {}
|
depd@2.0.0: {}
|
||||||
|
|
||||||
dope2@git+https://deemz.org/git/joeri/dope2.git#d75bf9f0f200769a5248ace8e4e2ace04fd60381:
|
dope2@git+https://deemz.org/git/joeri/dope2.git#0096bb5559224b4c9bbe74317e07dc71cfc09c70:
|
||||||
dependencies:
|
dependencies:
|
||||||
functional-red-black-tree: 1.0.1
|
functional-red-black-tree: 1.0.1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,20 +3,23 @@ import './App.css';
|
||||||
import { CommandContext } from './CommandContext';
|
import { CommandContext } from './CommandContext';
|
||||||
import { Editor, type EditorState } from './Editor';
|
import { Editor, type EditorState } from './Editor';
|
||||||
import { extendedEnv } from './EnvContext';
|
import { extendedEnv } from './EnvContext';
|
||||||
import { initialEditorState, nonEmptyEditorState, tripleFunctionCallEditorState } from "./configurations";
|
import { biggerExample, initialEditorState, nonEmptyEditorState, tripleFunctionCallEditorState } from "./configurations";
|
||||||
import { evalEditorBlock } from "./eval";
|
import { evalEditorBlock } from "./eval";
|
||||||
|
|
||||||
const commands: [string, string[], string][] = [
|
const commands: [string, string[], string][] = [
|
||||||
["call" , ['c' ], "call" ],
|
["call" , ['c' ], "call" ],
|
||||||
["eval" , ['e','Tab','Enter'], "eval" ],
|
["eval" , ['e','Tab','Enter'], "eval" ],
|
||||||
["transform", ['t', '.' ], "transform" ],
|
["transform", ['t', '.' ], "transform" ],
|
||||||
["let" , ['l', '=', 'a' ], "let ... in ..."],
|
["let" , ['l', '=' ], "let ... in ..."],
|
||||||
|
["lambda" , ['a' ], "lambda" ],
|
||||||
];
|
];
|
||||||
|
|
||||||
const examples: [string, EditorState][] = [
|
const examples: [string, EditorState][] = [
|
||||||
["empty editor", initialEditorState],
|
["empty editor", initialEditorState],
|
||||||
["push to list", nonEmptyEditorState],
|
["push to list", nonEmptyEditorState],
|
||||||
["function w/ 4 params", tripleFunctionCallEditorState]];
|
["function w/ 4 params", tripleFunctionCallEditorState],
|
||||||
|
["bigger example", biggerExample],
|
||||||
|
];
|
||||||
|
|
||||||
type AppState = {
|
type AppState = {
|
||||||
history: EditorState[],
|
history: EditorState[],
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,16 @@
|
||||||
.functionBlock {
|
.functionBlock {
|
||||||
border: solid 1px darkgray;
|
border: solid 1px darkgray;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 4px;
|
margin: 2px;
|
||||||
color: black;
|
color: black;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* * {
|
||||||
|
vertical-align: text-top;
|
||||||
|
} */
|
||||||
|
|
||||||
.functionName {
|
.functionName {
|
||||||
/* text-align: center; */
|
/* text-align: center; */
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
|
@ -14,11 +19,11 @@
|
||||||
.inputParam:after {
|
.inputParam:after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border: solid transparent;
|
right: 0;
|
||||||
border-width: 10px;
|
clip-path: polygon(1% 0%, 100% 50%, 0% 100%);
|
||||||
right: -19px;
|
height: 100%;
|
||||||
top: 0;
|
aspect-ratio: 0.25/1;
|
||||||
bottom:0;
|
transform: translateX(99%);
|
||||||
}
|
}
|
||||||
.inputParam {
|
.inputParam {
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
|
|
@ -30,7 +35,7 @@
|
||||||
|
|
||||||
/* Count nested level AFTER .outputParam (resets the depth) */
|
/* Count nested level AFTER .outputParam (resets the depth) */
|
||||||
.outputParam > .inputParam:after {
|
.outputParam > .inputParam:after {
|
||||||
border-left-color: rgb(242, 253, 146);
|
background-color: rgb(242, 253, 146);
|
||||||
}
|
}
|
||||||
.outputParam > .inputParam {
|
.outputParam > .inputParam {
|
||||||
background-color: rgb(242, 253, 146);
|
background-color: rgb(242, 253, 146);
|
||||||
|
|
@ -39,19 +44,19 @@
|
||||||
background-color: rgb(180, 248, 214);
|
background-color: rgb(180, 248, 214);
|
||||||
}
|
}
|
||||||
.outputParam > .inputParam > .inputParam:after {
|
.outputParam > .inputParam > .inputParam:after {
|
||||||
border-left-color: rgb(180, 248, 214);
|
background-color: rgb(180, 248, 214);
|
||||||
}
|
}
|
||||||
.outputParam > .inputParam > .inputParam > .inputParam {
|
.outputParam > .inputParam > .inputParam > .inputParam {
|
||||||
background-color: rgb(153, 212, 214);
|
background-color: rgb(153, 212, 214);
|
||||||
}
|
}
|
||||||
.outputParam > .inputParam > .inputParam > .inputParam:after {
|
.outputParam > .inputParam > .inputParam > .inputParam:after {
|
||||||
border-left-color: rgb(153, 212, 214);
|
background-color: rgb(153, 212, 214);
|
||||||
}
|
}
|
||||||
.outputParam > .inputParam > .inputParam > .inputParam > .inputParam {
|
.outputParam > .inputParam > .inputParam > .inputParam > .inputParam {
|
||||||
background-color: rgb(111, 186, 209);
|
background-color: rgb(111, 186, 209);
|
||||||
}
|
}
|
||||||
.outputParam > .inputParam > .inputParam > .inputParam > .inputParam:after {
|
.outputParam > .inputParam > .inputParam > .inputParam > .inputParam:after {
|
||||||
border-left-color: rgb(111, 186, 209);
|
background-color: rgb(111, 186, 209);
|
||||||
}
|
}
|
||||||
|
|
||||||
.typeAnnot {
|
.typeAnnot {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,30 @@
|
||||||
|
.editor {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
.typeSignature {
|
.typeSignature {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
background-color: white;
|
||||||
|
/* border: 1px solid black; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor:hover > .typeSignature {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.commandInput {
|
.commandInput {
|
||||||
width: 90px;
|
width: 30px;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.keyword {
|
||||||
|
color: blue;
|
||||||
|
font-weight: bold;
|
||||||
|
/* vertical-align: top; */
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
/* vertical-align: top; */
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { evalEditorBlock } from "./eval";
|
||||||
import { CommandContext } from "./CommandContext";
|
import { CommandContext } from "./CommandContext";
|
||||||
import "./Editor.css";
|
import "./Editor.css";
|
||||||
import { EnvContext } from "./EnvContext";
|
import { EnvContext } from "./EnvContext";
|
||||||
import type { LambdaBlockState } from "./LambdaBlock";
|
import { LambdaBlock, type LambdaBlockState } from "./LambdaBlock";
|
||||||
import { LetInBlock, type LetInBlockState } from "./LetInBlock";
|
import { LetInBlock, type LetInBlockState } from "./LetInBlock";
|
||||||
import { initialEditorState } from "./configurations";
|
import { initialEditorState } from "./configurations";
|
||||||
import { focusNextElement, focusPrevElement } from "./util/dom_trickery";
|
import { focusNextElement, focusPrevElement } from "./util/dom_trickery";
|
||||||
|
|
@ -71,9 +71,11 @@ export function Editor({state, setState, onCancel, suggestionPriority}: EditorPr
|
||||||
|
|
||||||
const globalContext = useContext(CommandContext);
|
const globalContext = useContext(CommandContext);
|
||||||
const onCommand = (e: React.KeyboardEvent) => {
|
const onCommand = (e: React.KeyboardEvent) => {
|
||||||
|
console.log(e);
|
||||||
|
|
||||||
// const type = getType(state.resolved);
|
// const type = getType(state.resolved);
|
||||||
// const commands = getCommands(type);
|
// const commands = getCommands(type);
|
||||||
const commands = ['e', 't', 'Enter', 'Backspace', 'ArrowLeft', 'ArrowRight', 'Tab', 'l', '=', '.', 'c'];
|
const commands = ['e', 't', 'Enter', 'Backspace', 'ArrowLeft', 'ArrowRight', 'Tab', 'l', 'L', '=', '.', 'c', 'a'];
|
||||||
if (!commands.includes(e.key)) {
|
if (!commands.includes(e.key)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -124,18 +126,35 @@ export function Editor({state, setState, onCancel, suggestionPriority}: EditorPr
|
||||||
}
|
}
|
||||||
// l -> Let ... in ...
|
// l -> Let ... in ...
|
||||||
// = -> assign to name
|
// = -> assign to name
|
||||||
if (e.key === 'l' || e.key === '=') {
|
if (e.key === 'l' || e.key === '=' && !e.shiftKey) {
|
||||||
// we become LetInBlock
|
// we become LetInBlock
|
||||||
setState(state => ({
|
setState(state => ({
|
||||||
kind: "let",
|
kind: "let",
|
||||||
inner: removeFocus(initialEditorState),
|
inner: removeFocus(initialEditorState),
|
||||||
name: "",
|
name: "",
|
||||||
value: removeFocus(state),
|
value: removeFocus(state),
|
||||||
resolved: undefined,
|
|
||||||
}));
|
}));
|
||||||
globalContext?.doHighlight.let();
|
globalContext?.doHighlight.let();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (e.key === 'L' || e.key === '=' && e.shiftKey) {
|
||||||
|
setState(state => ({
|
||||||
|
kind: "let",
|
||||||
|
inner: removeFocus(state),
|
||||||
|
name: "",
|
||||||
|
value: removeFocus(initialEditorState),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
// a -> lAmbdA
|
||||||
|
if (e.key === "a") {
|
||||||
|
setState(state => ({
|
||||||
|
kind: "lambda",
|
||||||
|
paramName: "",
|
||||||
|
expr: removeFocus(state),
|
||||||
|
}));
|
||||||
|
globalContext?.doHighlight.lambda();
|
||||||
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderBlock = () => {
|
const renderBlock = () => {
|
||||||
|
|
@ -169,22 +188,26 @@ export function Editor({state, setState, onCancel, suggestionPriority}: EditorPr
|
||||||
suggestionPriority={suggestionPriority}
|
suggestionPriority={suggestionPriority}
|
||||||
/>;
|
/>;
|
||||||
case "lambda":
|
case "lambda":
|
||||||
return <></>;
|
return <LambdaBlock
|
||||||
|
state={state}
|
||||||
|
setState={setState as (callback:(p:LambdaBlockState)=>EditorState)=>void}
|
||||||
|
suggestionPriority={suggestionPriority}
|
||||||
|
/>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const resolved = evalEditorBlock(state, env);
|
const resolved = evalEditorBlock(state, env);
|
||||||
return <>
|
return <span className="editor">
|
||||||
{renderBlock()}
|
{renderBlock()}
|
||||||
<div className="typeSignature">
|
<div className="typeSignature">
|
||||||
:: <Type type={getType(resolved)} />
|
<Type type={getType(resolved)} />
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
ref={commandInputRef}
|
ref={commandInputRef}
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
className="editable commandInput"
|
className="editable commandInput"
|
||||||
placeholder={`<command>`}
|
placeholder={`<c>`}
|
||||||
onKeyDown={onCommand}
|
onKeyDown={onCommand}
|
||||||
value={""}
|
value={""}
|
||||||
onChange={() => {}} />
|
onChange={() => {}} />
|
||||||
</>;
|
</span>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,13 +40,13 @@ interface InputBlockProps extends State2Props<InputBlockState> {
|
||||||
const computeSuggestions = (text, env, suggestionPriority: (s: SuggestionType) => number): PrioritizedSuggestionType[] => {
|
const computeSuggestions = (text, env, suggestionPriority: (s: SuggestionType) => number): PrioritizedSuggestionType[] => {
|
||||||
const literals = attemptParseLiteral(text);
|
const literals = attemptParseLiteral(text);
|
||||||
|
|
||||||
const ls = [
|
const ls: SuggestionType[] = [
|
||||||
// literals
|
// literals
|
||||||
... literals.map((lit) => ["literal", text, lit]),
|
... literals.map((lit) => ["literal", text, lit]),
|
||||||
|
|
||||||
// names
|
// names
|
||||||
... trie.suggest(env.name2dyn)(text)(Infinity)
|
... trie.suggest(env.name2dyn)(text)(Infinity)
|
||||||
.map(([name,type]) => ["name", name, type]),
|
.map(([name,type]) => ["name", name, {...type, substitutions: new Map()}]),
|
||||||
]
|
]
|
||||||
// return ls;
|
// return ls;
|
||||||
return ls
|
return ls
|
||||||
|
|
|
||||||
0
src/LambdaBlock.css
Normal file
0
src/LambdaBlock.css
Normal file
|
|
@ -1,6 +1,12 @@
|
||||||
import type { EditorState } from "./Editor";
|
import { useContext, useEffect, useRef } from "react";
|
||||||
import type { Dynamic } from "./eval";
|
|
||||||
import type { ResolvedType } from "./eval";
|
import { Editor, type EditorState, type State2Props } from "./Editor";
|
||||||
|
import type { SuggestionType } from "./InputBlock";
|
||||||
|
import { EnvContext } from "./EnvContext";
|
||||||
|
import { growEnv, TYPE_VARS } from "dope2";
|
||||||
|
import { autoInputWidth } from "./util/dom_trickery";
|
||||||
|
|
||||||
|
import "./LambdaBlock.css";
|
||||||
|
|
||||||
|
|
||||||
export interface LambdaBlockState {
|
export interface LambdaBlockState {
|
||||||
|
|
@ -9,6 +15,66 @@ export interface LambdaBlockState {
|
||||||
expr: EditorState;
|
expr: EditorState;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LambdaBlock {
|
interface LambdaBlockProps<
|
||||||
|
FnState=EditorState,
|
||||||
|
InputState=EditorState,
|
||||||
|
> extends State2Props<LambdaBlockState,EditorState> {
|
||||||
|
suggestionPriority: (suggestion: SuggestionType) => number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function LambdaBlock({state, setState, suggestionPriority}: LambdaBlockProps) {
|
||||||
|
const env = useContext(EnvContext);
|
||||||
|
const nameRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
const setParamName = paramName => setState(state => ({
|
||||||
|
...state,
|
||||||
|
paramName,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const setExpr = callback => setState(state => ({
|
||||||
|
...state,
|
||||||
|
expr: callback(state.expr),
|
||||||
|
}));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
nameRef.current?.focus();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => autoInputWidth(nameRef, state.paramName, 60), [nameRef, state.paramName]);
|
||||||
|
|
||||||
|
const innerEnv = growEnv(env)(state.paramName)({
|
||||||
|
kind: "unknown",
|
||||||
|
i: undefined,
|
||||||
|
t: TYPE_VARS[0],
|
||||||
|
});
|
||||||
|
|
||||||
|
return <span>
|
||||||
|
<span className="keyword">λ</span>
|
||||||
|
|
||||||
|
<span className="lambdaInputParam">
|
||||||
|
<input
|
||||||
|
ref={nameRef}
|
||||||
|
className='editable'
|
||||||
|
value={state.paramName}
|
||||||
|
placeholder="<name>"
|
||||||
|
onChange={e => setParamName(e.target.value)}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span className="keyword">:</span>
|
||||||
|
|
||||||
|
<span className="lambdaExpr">
|
||||||
|
<EnvContext value={innerEnv}>
|
||||||
|
<Editor
|
||||||
|
state={state.expr}
|
||||||
|
setState={setExpr}
|
||||||
|
onCancel={() => setState(state => state.expr)}
|
||||||
|
suggestionPriority={(suggestion: SuggestionType) => {
|
||||||
|
return suggestionPriority(suggestion);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</EnvContext>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,17 @@
|
||||||
|
.letIn {
|
||||||
.keyword {
|
display: inline-block;
|
||||||
color: blue;
|
border: 1px solid darkgrey;
|
||||||
/* margin: 0 2px 0 2px; */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.decl {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto auto auto auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column.rightAlign {
|
||||||
|
/* text-align: right; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.column {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
import { useContext, useEffect, useRef } from "react";
|
import { useContext, useEffect, useRef } from "react";
|
||||||
|
|
||||||
import { growEnv } from "dope2";
|
|
||||||
|
|
||||||
import { Editor, type EditorState } from "./Editor";
|
import { Editor, type EditorState } from "./Editor";
|
||||||
import { EnvContext } from "./EnvContext";
|
import { EnvContext } from "./EnvContext";
|
||||||
import { evalEditorBlock, evalLetInBlock, scoreResolved, type ResolvedType } from "./eval";
|
import { evalEditorBlock, evalLetInBlock, makeInnerEnv, scoreResolved } from "./eval";
|
||||||
import { type State2Props } from "./Editor";
|
import { type State2Props } from "./Editor";
|
||||||
import { autoInputWidth } from "./util/dom_trickery";
|
import { autoInputWidth } from "./util/dom_trickery";
|
||||||
|
|
||||||
|
|
@ -22,40 +21,54 @@ interface LetInBlockProps extends State2Props<LetInBlockState,EditorState> {
|
||||||
suggestionPriority: (suggestion: SuggestionType) => number;
|
suggestionPriority: (suggestion: SuggestionType) => number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeInnerEnv(env, name: string, value: ResolvedType) {
|
export function LetInBlock({state, setState, suggestionPriority}: LetInBlockProps) {
|
||||||
|
return <span className="letIn">
|
||||||
if (value.kind === "value") {
|
<div className="decl">
|
||||||
return growEnv(env)(name)(value)
|
<DeclColumns
|
||||||
}
|
state={state}
|
||||||
return env;
|
setState={setState}
|
||||||
|
suggestionPriority={suggestionPriority}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="inner">
|
||||||
|
<InnerMost
|
||||||
|
state={state}
|
||||||
|
setState={setState}
|
||||||
|
suggestionPriority={suggestionPriority}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LetInBlock({state, setState, suggestionPriority}: LetInBlockProps) {
|
function DeclColumns({state, setState, suggestionPriority}) {
|
||||||
const {name, value, inner} = state;
|
|
||||||
const env = useContext(EnvContext);
|
const env = useContext(EnvContext);
|
||||||
const valueResolved = evalEditorBlock(value, env);
|
const {name, value, inner} = state;
|
||||||
const innerEnv = makeInnerEnv(env, name, valueResolved);
|
|
||||||
const nameRef = useRef<HTMLInputElement>(null);
|
|
||||||
|
|
||||||
const setInner = callback => setState(state => ({...state, inner: callback(state.inner)}));
|
const setInner = callback => setState(state => ({...state, inner: callback(state.inner)}));
|
||||||
const setValue = callback => setState(state => ({...state, value: callback(state.value)}));
|
const setValue = callback => setState(state => ({...state, value: callback(state.value)}));
|
||||||
|
|
||||||
const onChangeName = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const onChangeName = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setState(state => ({...state, name: e.target.value}));
|
setState(state => ({...state, name: e.target.value}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const valueSuggestionPriority = (suggestion: SuggestionType) => {
|
||||||
|
const innerEnv = makeInnerEnv(env, name, suggestion[2]);
|
||||||
|
const resolved = evalEditorBlock(inner, innerEnv);
|
||||||
|
return scoreResolved(resolved, suggestionPriority);
|
||||||
|
};
|
||||||
|
const innerSuggestionPriority = suggestionPriority;
|
||||||
|
|
||||||
|
const nameRef = useRef<HTMLInputElement>(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
nameRef.current?.focus();
|
nameRef.current?.focus();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => autoInputWidth(nameRef, name, 60), [nameRef, name]);
|
useEffect(() => autoInputWidth(nameRef, name, 60), [nameRef, name]);
|
||||||
|
|
||||||
console.log({innerEnv});
|
const valueResolved = evalEditorBlock(state.value, env);
|
||||||
|
const innerEnv = makeInnerEnv(env, state.name, valueResolved);
|
||||||
|
|
||||||
return <span className="letIn">
|
return <>
|
||||||
<div className="decl">
|
<span className="keyword column">let </span>
|
||||||
<span className="keyword">let</span>
|
<span className="column rightAlign">
|
||||||
|
|
||||||
<input
|
<input
|
||||||
ref={nameRef}
|
ref={nameRef}
|
||||||
className='editable'
|
className='editable'
|
||||||
|
|
@ -63,30 +76,52 @@ export function LetInBlock({state, setState, suggestionPriority}: LetInBlockProp
|
||||||
placeholder="<name>"
|
placeholder="<name>"
|
||||||
onChange={onChangeName}
|
onChange={onChangeName}
|
||||||
/>
|
/>
|
||||||
<span className="keyword"> = </span>
|
</span>
|
||||||
<Editor
|
<span className="keyword column"> = </span>
|
||||||
state={value}
|
<span className="column">
|
||||||
setState={setValue}
|
|
||||||
suggestionPriority={(suggestion: SuggestionType) => {
|
|
||||||
const innerEnv = makeInnerEnv(env, name, suggestion[2]);
|
|
||||||
const resolved = evalEditorBlock(inner, innerEnv);
|
|
||||||
return scoreResolved(resolved, suggestionPriority);
|
|
||||||
}}
|
|
||||||
onCancel={() => setState(state => state.inner)} // keep inner
|
|
||||||
/>
|
|
||||||
<span className="keyword">in</span>
|
|
||||||
</div>
|
|
||||||
<div className="inner">
|
|
||||||
<EnvContext value={innerEnv}>
|
|
||||||
<Editor
|
<Editor
|
||||||
|
state={value}
|
||||||
|
setState={setValue}
|
||||||
|
suggestionPriority={valueSuggestionPriority}
|
||||||
|
onCancel={() => setState(state => state.inner)} // keep inner
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
{/* <span className="keyword column">in</span> */}
|
||||||
|
{inner.kind === "let" &&
|
||||||
|
<EnvContext value={innerEnv}>
|
||||||
|
<DeclColumns
|
||||||
state={inner}
|
state={inner}
|
||||||
setState={setInner}
|
setState={setInner}
|
||||||
suggestionPriority={(suggestion: SuggestionType) => {
|
suggestionPriority={innerSuggestionPriority}
|
||||||
return suggestionPriority(suggestion)
|
|
||||||
}}
|
|
||||||
onCancel={() => setState(state => state.value)} // keep value
|
|
||||||
/>
|
/>
|
||||||
</EnvContext>
|
</EnvContext>
|
||||||
</div>
|
}
|
||||||
</span>
|
</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function InnerMost({state, setState, suggestionPriority}) {
|
||||||
|
const env = useContext(EnvContext);
|
||||||
|
const setInner = callback => setState(state => ({...state, inner: callback(state.inner)}));
|
||||||
|
const valueResolved = evalEditorBlock(state.value, env);
|
||||||
|
const innerEnv = makeInnerEnv(env, state.name, valueResolved);
|
||||||
|
const onCancel = () => setState(state => state.value);
|
||||||
|
if (state.inner.kind === "let") {
|
||||||
|
return <EnvContext value={innerEnv}>
|
||||||
|
<InnerMost
|
||||||
|
state={state.inner}
|
||||||
|
setState={setInner}
|
||||||
|
suggestionPriority={suggestionPriority}
|
||||||
|
/>
|
||||||
|
</EnvContext>;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return <EnvContext value={innerEnv}>
|
||||||
|
<Editor
|
||||||
|
state={state.inner}
|
||||||
|
setState={setInner}
|
||||||
|
suggestionPriority={suggestionPriority}
|
||||||
|
onCancel={onCancel} // keep value
|
||||||
|
/>
|
||||||
|
</EnvContext>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,3 +7,13 @@
|
||||||
padding-right: 2px;
|
padding-right: 2px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.valueUUID {
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: lightyellow;
|
||||||
|
border: 1px solid black;
|
||||||
|
margin-left: 2px;
|
||||||
|
margin-right: 2px;
|
||||||
|
padding-left: 2px;
|
||||||
|
padding-right: 2px;
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import {getType, getInst, getSymbol, Double, Int, symbolFunction, symbolProduct, symbolSum, symbolDict, symbolSet, symbolList, eqType, match, getLeft, getRight, dict, Bool, set, Unit} from "dope2";
|
import {getType, getInst, getSymbol, Double, Int, symbolFunction, symbolProduct, symbolSum, symbolDict, symbolSet, symbolList, eqType, match, getLeft, getRight, dict, Bool, set, Unit, symbolType, symbolUUID, getHumanReadableName} from "dope2";
|
||||||
|
|
||||||
import "./Value.css";
|
import "./Value.css";
|
||||||
|
import { Type } from "./Type";
|
||||||
|
|
||||||
export function Value({dynamic}) {
|
export function Value({dynamic}) {
|
||||||
const type = getType(dynamic);
|
const type = getType(dynamic);
|
||||||
|
|
@ -32,7 +33,10 @@ export function Value({dynamic}) {
|
||||||
return <ValueSet val={inst} elemType={type.params[0](type)} />;
|
return <ValueSet val={inst} elemType={type.params[0](type)} />;
|
||||||
case symbolList:
|
case symbolList:
|
||||||
return <ValueList val={inst} elemType={type.params[0](type)} />;
|
return <ValueList val={inst} elemType={type.params[0](type)} />;
|
||||||
|
case symbolType:
|
||||||
|
return <Type type={inst}/>;
|
||||||
|
case symbolUUID:
|
||||||
|
return <ValueUUID val={inst}/>
|
||||||
default:
|
default:
|
||||||
return <>don't know how to show value</>;
|
return <>don't know how to show value</>;
|
||||||
}
|
}
|
||||||
|
|
@ -74,3 +78,6 @@ function ValueProduct({val, leftType, rightType}) {
|
||||||
function ValueUnit() {
|
function ValueUnit() {
|
||||||
return <>{'()'}</>;
|
return <>{'()'}</>;
|
||||||
}
|
}
|
||||||
|
function ValueUUID({val}) {
|
||||||
|
return <span className="valueUUID">{getHumanReadableName(val)}</span>;
|
||||||
|
}
|
||||||
|
|
@ -74,3 +74,182 @@ export const tripleFunctionCallEditorState: EditorState = {
|
||||||
focus: false,
|
focus: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const biggerExample: EditorState = {
|
||||||
|
"kind": "let",
|
||||||
|
"inner": {
|
||||||
|
"kind": "let",
|
||||||
|
"inner": {
|
||||||
|
"kind": "let",
|
||||||
|
"inner": {
|
||||||
|
"kind": "let",
|
||||||
|
"inner": {
|
||||||
|
"kind": "input",
|
||||||
|
"text": "",
|
||||||
|
"value": {
|
||||||
|
"kind": "text"
|
||||||
|
},
|
||||||
|
"focus": false
|
||||||
|
},
|
||||||
|
"name": "myListInc",
|
||||||
|
"value": {
|
||||||
|
"kind": "call",
|
||||||
|
"fn": {
|
||||||
|
"kind": "call",
|
||||||
|
"fn": {
|
||||||
|
"kind": "input",
|
||||||
|
"text": "list.map",
|
||||||
|
"value": {
|
||||||
|
"kind": "name"
|
||||||
|
},
|
||||||
|
"focus": false
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"kind": "input",
|
||||||
|
"text": "myList",
|
||||||
|
"value": {
|
||||||
|
"kind": "name"
|
||||||
|
},
|
||||||
|
"focus": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"kind": "input",
|
||||||
|
"text": "inc",
|
||||||
|
"value": {
|
||||||
|
"kind": "name"
|
||||||
|
},
|
||||||
|
"focus": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "myList",
|
||||||
|
"value": {
|
||||||
|
"kind": "call",
|
||||||
|
"fn": {
|
||||||
|
"kind": "call",
|
||||||
|
"fn": {
|
||||||
|
"kind": "input",
|
||||||
|
"text": "list.push",
|
||||||
|
"value": {
|
||||||
|
"kind": "name"
|
||||||
|
},
|
||||||
|
"focus": false
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"kind": "call",
|
||||||
|
"fn": {
|
||||||
|
"kind": "call",
|
||||||
|
"fn": {
|
||||||
|
"kind": "input",
|
||||||
|
"text": "list.push",
|
||||||
|
"value": {
|
||||||
|
"kind": "name"
|
||||||
|
},
|
||||||
|
"focus": false
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"kind": "call",
|
||||||
|
"fn": {
|
||||||
|
"kind": "call",
|
||||||
|
"fn": {
|
||||||
|
"kind": "input",
|
||||||
|
"text": "list.push",
|
||||||
|
"value": {
|
||||||
|
"kind": "name"
|
||||||
|
},
|
||||||
|
"focus": false
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"kind": "input",
|
||||||
|
"text": "list.emptyList",
|
||||||
|
"value": {
|
||||||
|
"kind": "name"
|
||||||
|
},
|
||||||
|
"focus": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"kind": "input",
|
||||||
|
"text": "1",
|
||||||
|
"value": {
|
||||||
|
"kind": "literal",
|
||||||
|
"type": "Int"
|
||||||
|
},
|
||||||
|
"focus": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"kind": "input",
|
||||||
|
"text": "2",
|
||||||
|
"value": {
|
||||||
|
"kind": "literal",
|
||||||
|
"type": "Int"
|
||||||
|
},
|
||||||
|
"focus": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"kind": "input",
|
||||||
|
"text": "3",
|
||||||
|
"value": {
|
||||||
|
"kind": "literal",
|
||||||
|
"type": "Int"
|
||||||
|
},
|
||||||
|
"focus": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "id",
|
||||||
|
"value": {
|
||||||
|
"kind": "lambda",
|
||||||
|
"paramName": "x",
|
||||||
|
"expr": {
|
||||||
|
"kind": "input",
|
||||||
|
"text": "x",
|
||||||
|
"value": {
|
||||||
|
"kind": "name"
|
||||||
|
},
|
||||||
|
"focus": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "inc",
|
||||||
|
"value": {
|
||||||
|
"kind": "lambda",
|
||||||
|
"paramName": "x",
|
||||||
|
"expr": {
|
||||||
|
"kind": "call",
|
||||||
|
"fn": {
|
||||||
|
"kind": "call",
|
||||||
|
"fn": {
|
||||||
|
"kind": "input",
|
||||||
|
"text": "addInt",
|
||||||
|
"value": {
|
||||||
|
"kind": "name"
|
||||||
|
},
|
||||||
|
"focus": false
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"kind": "input",
|
||||||
|
"text": "x",
|
||||||
|
"value": {
|
||||||
|
"kind": "name"
|
||||||
|
},
|
||||||
|
"focus": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"kind": "input",
|
||||||
|
"text": "1",
|
||||||
|
"value": {
|
||||||
|
"kind": "literal",
|
||||||
|
"type": "Int"
|
||||||
|
},
|
||||||
|
"focus": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
120
src/eval.ts
120
src/eval.ts
|
|
@ -1,29 +1,36 @@
|
||||||
import { apply, assignFn, Double, getSymbol, Int, makeGeneric, NotAFunctionError, prettyT, symbolFunction, trie, UnifyError } from "dope2";
|
import { assignFnSubstitutions, Double, fnType, getSymbol, growEnv, Int, makeGeneric, NotAFunctionError, prettyT, substitute, symbolFunction, trie, TYPE_VARS, UnifyError } from "dope2";
|
||||||
|
|
||||||
import type { EditorState } from "./Editor";
|
import type { EditorState } from "./Editor";
|
||||||
import type { InputValueType } from "./InputBlock";
|
import type { InputValueType, SuggestionType } from "./InputBlock";
|
||||||
import { makeInnerEnv } from "./LetInBlock";
|
|
||||||
|
interface Type {
|
||||||
|
symbol: string;
|
||||||
|
params: any[];
|
||||||
|
};
|
||||||
|
|
||||||
export interface DeepError {
|
export interface DeepError {
|
||||||
kind: "error";
|
kind: "error";
|
||||||
e: Error;
|
e: Error;
|
||||||
depth: number;
|
depth: number;
|
||||||
t: any;
|
t: Type;
|
||||||
|
substitutions: Map<Type,Type>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// a dynamically typed value = tuple (instance, type)
|
// a dynamically typed value = tuple (instance, type)
|
||||||
export interface Dynamic {
|
export interface Dynamic {
|
||||||
kind: "value",
|
kind: "value",
|
||||||
i: any;
|
i: any;
|
||||||
t: any;
|
t: Type;
|
||||||
|
substitutions: Map<Type,Type>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface Unknown {
|
export interface Unknown {
|
||||||
kind: "unknown";
|
kind: "unknown";
|
||||||
t: any;
|
t: Type;
|
||||||
|
substitutions: Map<Type,Type>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const entirelyUnknown: Unknown = { kind: "unknown", t: makeGeneric(a => a) };
|
export const entirelyUnknown: Unknown = { kind: "unknown", t: makeGeneric(a => a), substitutions: new Map() };
|
||||||
|
|
||||||
// the value of every block is either known (Dynamic), an error, or unknown
|
// the value of every block is either known (Dynamic), an error, or unknown
|
||||||
export type ResolvedType = Dynamic | DeepError | Unknown;
|
export type ResolvedType = Dynamic | DeepError | Unknown;
|
||||||
|
|
@ -41,8 +48,8 @@ export const evalEditorBlock = (s: EditorState, env): ResolvedType => {
|
||||||
return evalLetInBlock(s.value, s.name, s.inner, env);
|
return evalLetInBlock(s.value, s.name, s.inner, env);
|
||||||
}
|
}
|
||||||
if (s.kind === "lambda") {
|
if (s.kind === "lambda") {
|
||||||
const expr = evalEditorBlock(s.expr, env);
|
return evalLambdaBlock(s.paramName, s.expr, env);
|
||||||
// todo
|
|
||||||
}
|
}
|
||||||
return entirelyUnknown; // todo
|
return entirelyUnknown; // todo
|
||||||
};
|
};
|
||||||
|
|
@ -54,7 +61,11 @@ export function evalInputBlock(text: string, value: InputValueType, env): Resolv
|
||||||
else if (value.kind === "name") {
|
else if (value.kind === "name") {
|
||||||
const found = trie.get(env.name2dyn)(text);
|
const found = trie.get(env.name2dyn)(text);
|
||||||
if (found) {
|
if (found) {
|
||||||
return { kind: "value", ...found };
|
return {
|
||||||
|
kind: found.kind || "value",
|
||||||
|
...found,
|
||||||
|
substitutions: new Map(),
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
return entirelyUnknown;
|
return entirelyUnknown;
|
||||||
}
|
}
|
||||||
|
|
@ -64,6 +75,10 @@ export function evalInputBlock(text: string, value: InputValueType, env): Resolv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mergeMaps = (...maps: Map<Type,Type>[]) => {
|
||||||
|
return new Map(maps.flatMap(m => [...m]));
|
||||||
|
}
|
||||||
|
|
||||||
export function evalCallBlock(fn: ResolvedType, input: ResolvedType): ResolvedType {
|
export function evalCallBlock(fn: ResolvedType, input: ResolvedType): ResolvedType {
|
||||||
if (getSymbol(fn.t) !== symbolFunction) {
|
if (getSymbol(fn.t) !== symbolFunction) {
|
||||||
if (fn.kind === "unknown") {
|
if (fn.kind === "unknown") {
|
||||||
|
|
@ -74,12 +89,15 @@ export function evalCallBlock(fn: ResolvedType, input: ResolvedType): ResolvedTy
|
||||||
kind: "error",
|
kind: "error",
|
||||||
e: new NotAFunctionError(`${prettyT(fn.t)} is not a function type!`),
|
e: new NotAFunctionError(`${prettyT(fn.t)} is not a function type!`),
|
||||||
t: entirelyUnknown.t,
|
t: entirelyUnknown.t,
|
||||||
|
substitutions: mergeMaps(fn.substitutions, input.substitutions),
|
||||||
depth: 0,
|
depth: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// fn is a function...
|
// fn is a function...
|
||||||
const outType = assignFn(fn.t, input.t); // may throw
|
const [outType, substitutions] = assignFnSubstitutions(fn.t, input.t); // may throw
|
||||||
|
|
||||||
|
const mergedSubstitutions = mergeMaps(substitutions, fn.substitutions, input.substitutions);
|
||||||
|
|
||||||
if (input.kind === "error") {
|
if (input.kind === "error") {
|
||||||
return {
|
return {
|
||||||
|
|
@ -87,6 +105,7 @@ export function evalCallBlock(fn: ResolvedType, input: ResolvedType): ResolvedTy
|
||||||
e: input.e, // bubble up the error
|
e: input.e, // bubble up the error
|
||||||
depth: 0,
|
depth: 0,
|
||||||
t: outType,
|
t: outType,
|
||||||
|
substitutions: mergedSubstitutions,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (fn.kind === "error") {
|
if (fn.kind === "error") {
|
||||||
|
|
@ -96,16 +115,26 @@ export function evalCallBlock(fn: ResolvedType, input: ResolvedType): ResolvedTy
|
||||||
e: fn.e,
|
e: fn.e,
|
||||||
depth: fn.depth+1,
|
depth: fn.depth+1,
|
||||||
t: outType,
|
t: outType,
|
||||||
|
substitutions: mergedSubstitutions,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// if the above statement did not throw => types are compatible...
|
// if the above statement did not throw => types are compatible...
|
||||||
if (input.kind === "value" && fn.kind === "value") {
|
if (input.kind === "value" && fn.kind === "value") {
|
||||||
const outValue = fn.i(input.i);
|
const outValue = fn.i(input.i);
|
||||||
return { kind: "value", i: outValue, t: outType };
|
return {
|
||||||
|
kind: "value",
|
||||||
|
i: outValue,
|
||||||
|
t: outType,
|
||||||
|
substitutions: mergedSubstitutions,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// we don't know the value, but we do know the type:
|
// we don't know the value, but we do know the type:
|
||||||
return { kind: "unknown", t: outType };
|
return {
|
||||||
|
kind: "unknown",
|
||||||
|
t: outType,
|
||||||
|
substitutions: mergedSubstitutions,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
|
@ -117,18 +146,49 @@ export function evalCallBlock(fn: ResolvedType, input: ResolvedType): ResolvedTy
|
||||||
e,
|
e,
|
||||||
depth: 0,
|
depth: 0,
|
||||||
t: outType,
|
t: outType,
|
||||||
|
substitutions: mergeMaps(fn.substitutions, input.substitutions),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function evalLetInBlock(value: EditorState, name: string, inner: EditorState, env) {
|
export function evalLetInBlock(value: EditorState, name: string, inner: EditorState, env): ResolvedType {
|
||||||
const valueResolved = evalEditorBlock(value, env);
|
const valueResolved = evalEditorBlock(value, env);
|
||||||
const innerEnv = makeInnerEnv(env, name, valueResolved)
|
const innerEnv = makeInnerEnv(env, name, valueResolved)
|
||||||
return evalEditorBlock(inner, innerEnv);
|
return evalEditorBlock(inner, innerEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function evalLambdaBlock(paramName: string, expr: EditorState, env): ResolvedType {
|
||||||
|
const fn = (x: any) => {
|
||||||
|
const innerEnv = makeInnerEnv(env, paramName, {
|
||||||
|
kind: "value",
|
||||||
|
i: x,
|
||||||
|
t: TYPE_VARS[0],
|
||||||
|
substitutions: new Map(),
|
||||||
|
});
|
||||||
|
const result = evalEditorBlock(expr, innerEnv);
|
||||||
|
if (result.kind === "value") {
|
||||||
|
return result.i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// static env: we only know the name and the type
|
||||||
|
const staticInnerEnv = makeInnerEnv(env, paramName, {
|
||||||
|
kind: "unknown", // parameter value is not statically known
|
||||||
|
t: TYPE_VARS[0],
|
||||||
|
substitutions: new Map(),
|
||||||
|
});
|
||||||
|
const abstractOutput = evalEditorBlock(expr, staticInnerEnv);
|
||||||
|
const t = fnType(_ => entirelyUnknown.t)(_ => abstractOutput.t);
|
||||||
|
const T = substitute(t, abstractOutput.substitutions, [])
|
||||||
|
return {
|
||||||
|
kind: "value",
|
||||||
|
t: T,
|
||||||
|
i: fn,
|
||||||
|
substitutions: new Map(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function haveValue(resolved: ResolvedType) {
|
export function haveValue(resolved: ResolvedType) {
|
||||||
// return resolved && !(resolved instanceof DeepError);
|
// return resolved && !(resolved instanceof DeepError);
|
||||||
return resolved.kind === "value";
|
return resolved.kind === "value";
|
||||||
|
|
@ -149,7 +209,12 @@ function parseAsDouble(text: string): ResolvedType {
|
||||||
if (text !== '') {
|
if (text !== '') {
|
||||||
const num = Number(text);
|
const num = Number(text);
|
||||||
if (!Number.isNaN(num)) {
|
if (!Number.isNaN(num)) {
|
||||||
return { kind: "value", i: num, t: Double };
|
return {
|
||||||
|
kind: "value",
|
||||||
|
i: num,
|
||||||
|
t: Double,
|
||||||
|
substitutions: new Map(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return entirelyUnknown;
|
return entirelyUnknown;
|
||||||
|
|
@ -157,7 +222,12 @@ function parseAsDouble(text: string): ResolvedType {
|
||||||
function parseAsInt(text: string): ResolvedType {
|
function parseAsInt(text: string): ResolvedType {
|
||||||
if (text !== '') {
|
if (text !== '') {
|
||||||
try {
|
try {
|
||||||
return { kind: "value", i: BigInt(text), t: Int }; // may throw
|
return {
|
||||||
|
kind: "value",
|
||||||
|
i: BigInt(text),
|
||||||
|
t: Int,
|
||||||
|
substitutions: new Map(),
|
||||||
|
}; // may throw
|
||||||
}
|
}
|
||||||
catch {}
|
catch {}
|
||||||
}
|
}
|
||||||
|
|
@ -171,10 +241,14 @@ export function attemptParseLiteral(text: string): Dynamic[] {
|
||||||
.filter(resolved => (resolved.kind !== "unknown" && resolved.kind !== "error")) as unknown as Dynamic[];
|
.filter(resolved => (resolved.kind !== "unknown" && resolved.kind !== "error")) as unknown as Dynamic[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function scoreResolved(resolved: ResolvedType, outPriority) {
|
export function scoreResolved(resolved: ResolvedType, outPriority: (s:SuggestionType) => number) {
|
||||||
const bias = outPriority(['literal', '<computed>',
|
const bias = outPriority(['literal', '<computed>',
|
||||||
// @ts-ignore: // TODO fix this
|
{
|
||||||
{t: resolved.t}]);
|
// @ts-ignore
|
||||||
|
kind: "unknown",
|
||||||
|
t: resolved.t,
|
||||||
|
substitutions: new Map(),
|
||||||
|
}]);
|
||||||
|
|
||||||
if (resolved.kind === "value") {
|
if (resolved.kind === "value") {
|
||||||
return 1 + bias;
|
return 1 + bias;
|
||||||
|
|
@ -189,3 +263,11 @@ export function scoreResolved(resolved: ResolvedType, outPriority) {
|
||||||
return -2 + bias; // even worse: fn is not a function!
|
return -2 + bias; // even worse: fn is not a function!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function makeInnerEnv(env, name: string, value: ResolvedType) {
|
||||||
|
if (name !== "" && value.kind === "value") {
|
||||||
|
return growEnv(env)(name)(value);
|
||||||
|
}
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue