use unused typevar when encountering unknown value or lambda parameter - type inferencing still not entirely correct
This commit is contained in:
parent
d7a4e210a2
commit
496463bbac
8 changed files with 94 additions and 66 deletions
|
|
@ -64,13 +64,9 @@
|
||||||
.outputParam {
|
.outputParam {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
/* margin-left: 28px; */
|
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
/* padding-left: 14px; */
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
/* border: solid 2px orange; */
|
|
||||||
background-color: rgb(233, 224, 205);
|
background-color: rgb(233, 224, 205);
|
||||||
/* border-radius: 10px; */
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,28 +79,28 @@
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam:after {
|
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam:after {
|
||||||
border-left-color: pink;
|
background-color: pink;
|
||||||
}
|
}
|
||||||
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam {
|
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam {
|
||||||
background-color: pink;
|
background-color: pink;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam:after {
|
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam:after {
|
||||||
border-left-color: pink;
|
background-color: pink;
|
||||||
}
|
}
|
||||||
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam > .inputParam {
|
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam > .inputParam {
|
||||||
background-color: pink;
|
background-color: pink;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam > .inputParam:after {
|
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam > .inputParam:after {
|
||||||
border-left-color: pink;
|
background-color: pink;
|
||||||
}
|
}
|
||||||
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam > .inputParam > .inputParam {
|
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam > .inputParam > .inputParam {
|
||||||
background-color: pink;
|
background-color: pink;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam > .inputParam > .inputParam:after {
|
.functionBlock.unifyError > .functionParams > .outputParam > .inputParam > .inputParam > .inputParam > .inputParam:after {
|
||||||
border-left-color: pink;
|
background-color: pink;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inputParam.offending {
|
.inputParam.offending {
|
||||||
|
|
@ -112,5 +108,5 @@
|
||||||
color: white !important;
|
color: white !important;
|
||||||
}
|
}
|
||||||
.inputParam.offending:after {
|
.inputParam.offending:after {
|
||||||
border-left-color: darkred !important;
|
background-color: darkred !important;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { useContext, useInsertionEffect } from "react";
|
||||||
import { Editor, type EditorState } from "./Editor";
|
import { Editor, type EditorState } from "./Editor";
|
||||||
import { Value } from "./Value";
|
import { Value } from "./Value";
|
||||||
import { type SetStateFn, type State2Props } from "./Editor";
|
import { type SetStateFn, type State2Props } from "./Editor";
|
||||||
import { evalCallBlock, evalEditorBlock, scoreResolved } from "./eval";
|
import { evalCallBlock, evalCallBlock2, evalEditorBlock, scoreResolved } from "./eval";
|
||||||
import { type ResolvedType } from "./eval";
|
import { type ResolvedType } from "./eval";
|
||||||
import "./CallBlock.css";
|
import "./CallBlock.css";
|
||||||
import { EnvContext } from "./EnvContext";
|
import { EnvContext } from "./EnvContext";
|
||||||
|
|
@ -97,6 +97,7 @@ export function CallBlockNoSugar({ state, setState, suggestionPriority }: CallBl
|
||||||
evalEditorBlock(state.fn, env), // fn *may* be set
|
evalEditorBlock(state.fn, env), // fn *may* be set
|
||||||
inputSuggestion[2], // suggestions will be for input
|
inputSuggestion[2], // suggestions will be for input
|
||||||
suggestionPriority, // priority function we get from parent block
|
suggestionPriority, // priority function we get from parent block
|
||||||
|
env,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -105,8 +106,8 @@ export function CallBlockNoSugar({ state, setState, suggestionPriority }: CallBl
|
||||||
</span>;
|
</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function computePriority(fn: ResolvedType, input: ResolvedType, outPriority: (s: SuggestionType) => number) {
|
function computePriority(fn: ResolvedType, input: ResolvedType, outPriority: (s: SuggestionType) => number, env) {
|
||||||
const resolved = evalCallBlock(fn, input);
|
const resolved = evalCallBlock2(fn, input, env);
|
||||||
return scoreResolved(resolved, outPriority);
|
return scoreResolved(resolved, outPriority);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,6 +132,7 @@ function FunctionHeader({ fn, setFn, input, onFnCancel, suggestionPriority }) {
|
||||||
fnSuggestion[2],
|
fnSuggestion[2],
|
||||||
evalEditorBlock(fn.input, env),
|
evalEditorBlock(fn.input, env),
|
||||||
suggestionPriority,
|
suggestionPriority,
|
||||||
|
env,
|
||||||
)}
|
)}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
@ -153,6 +155,7 @@ function FunctionName({fn, setFn, onFnCancel, suggestionPriority, input}) {
|
||||||
fnSuggestion[2], // suggestions will be for function
|
fnSuggestion[2], // suggestions will be for function
|
||||||
evalEditorBlock(input, env), // input *may* be set
|
evalEditorBlock(input, env), // input *may* be set
|
||||||
suggestionPriority, // priority function we get from parent block
|
suggestionPriority, // priority function we get from parent block
|
||||||
|
env,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</span>;
|
</span>;
|
||||||
|
|
@ -180,6 +183,7 @@ function InputParams({ fn, setFn, input, setInput, onInputCancel, depth, errorDe
|
||||||
evalEditorBlock(fn.fn, env),
|
evalEditorBlock(fn.fn, env),
|
||||||
inputSuggestion[2],
|
inputSuggestion[2],
|
||||||
suggestionPriority,
|
suggestionPriority,
|
||||||
|
env,
|
||||||
)}
|
)}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
@ -199,6 +203,7 @@ function InputParams({ fn, setFn, input, setInput, onInputCancel, depth, errorDe
|
||||||
evalEditorBlock(fn, env), // fn *may* be set
|
evalEditorBlock(fn, env), // fn *may* be set
|
||||||
inputSuggestion[2], // suggestions will be for input
|
inputSuggestion[2], // suggestions will be for input
|
||||||
suggestionPriority, // priority function we get from parent block
|
suggestionPriority, // priority function we get from parent block
|
||||||
|
env,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>;
|
</div>;
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.typeSignature {
|
.typeSignature {
|
||||||
display: none;
|
display: inline-block;
|
||||||
position: absolute;
|
/* display: none; */
|
||||||
|
/* position: absolute; */
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
background-color: white;
|
/* background-color: white; */
|
||||||
/* border: 1px solid black; */
|
/* border: 1px solid black; */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,10 +71,6 @@ 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 commands = getCommands(type);
|
|
||||||
const commands = ['e', 't', 'Enter', 'Backspace', 'ArrowLeft', 'ArrowRight', 'Tab', 'l', 'L', '=', '.', 'c', 'a'];
|
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;
|
||||||
|
|
@ -199,7 +195,7 @@ export function Editor({state, setState, onCancel, suggestionPriority}: EditorPr
|
||||||
return <span className="editor">
|
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}
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ 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, env);
|
||||||
|
|
||||||
const ls: SuggestionType[] = [
|
const ls: SuggestionType[] = [
|
||||||
// literals
|
// literals
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
.lambdaExpr {
|
||||||
|
display: inline-block;
|
||||||
|
border: solid 1px darkgrey;
|
||||||
|
margin: 2px;
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ import { growEnv, TYPE_VARS } from "dope2";
|
||||||
import { autoInputWidth } from "./util/dom_trickery";
|
import { autoInputWidth } from "./util/dom_trickery";
|
||||||
|
|
||||||
import "./LambdaBlock.css";
|
import "./LambdaBlock.css";
|
||||||
|
import { getUnusedTypeVar } from "./eval";
|
||||||
|
|
||||||
|
|
||||||
export interface LambdaBlockState {
|
export interface LambdaBlockState {
|
||||||
|
|
@ -31,12 +32,18 @@ export function LambdaBlock({state, setState, suggestionPriority}: LambdaBlockPr
|
||||||
...state,
|
...state,
|
||||||
paramName,
|
paramName,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const setExpr = callback => setState(state => ({
|
const setExpr = callback => setState(state => ({
|
||||||
...state,
|
...state,
|
||||||
expr: callback(state.expr),
|
expr: callback(state.expr),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const onChangeName = (e) => {
|
||||||
|
if (state.paramName === "" && e.key === 'Backspace') {
|
||||||
|
setState(state => state.expr);
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
nameRef.current?.focus();
|
nameRef.current?.focus();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
@ -46,7 +53,7 @@ export function LambdaBlock({state, setState, suggestionPriority}: LambdaBlockPr
|
||||||
const innerEnv = growEnv(env)(state.paramName)({
|
const innerEnv = growEnv(env)(state.paramName)({
|
||||||
kind: "unknown",
|
kind: "unknown",
|
||||||
i: undefined,
|
i: undefined,
|
||||||
t: TYPE_VARS[0],
|
t: getUnusedTypeVar(env),
|
||||||
});
|
});
|
||||||
|
|
||||||
return <span>
|
return <span>
|
||||||
|
|
@ -58,13 +65,14 @@ export function LambdaBlock({state, setState, suggestionPriority}: LambdaBlockPr
|
||||||
className='editable'
|
className='editable'
|
||||||
value={state.paramName}
|
value={state.paramName}
|
||||||
placeholder="<name>"
|
placeholder="<name>"
|
||||||
|
onKeyDown={onChangeName}
|
||||||
onChange={e => setParamName(e.target.value)}
|
onChange={e => setParamName(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className="keyword">:</span>
|
<span className="keyword">:</span>
|
||||||
|
|
||||||
<span className="lambdaExpr">
|
<div className="lambdaExpr">
|
||||||
<EnvContext value={innerEnv}>
|
<EnvContext value={innerEnv}>
|
||||||
<Editor
|
<Editor
|
||||||
state={state.expr}
|
state={state.expr}
|
||||||
|
|
@ -75,6 +83,6 @@ export function LambdaBlock({state, setState, suggestionPriority}: LambdaBlockPr
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</EnvContext>
|
</EnvContext>
|
||||||
</span>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
97
src/eval.ts
97
src/eval.ts
|
|
@ -1,4 +1,4 @@
|
||||||
import { assignFnSubstitutions, Double, fnType, getSymbol, growEnv, Int, makeGeneric, NotAFunctionError, prettyT, substitute, symbolFunction, trie, TYPE_VARS, UnifyError } from "dope2";
|
import { assignFnSubstitutions, dict, Double, fnType, getSymbol, growEnv, Int, makeGeneric, NotAFunctionError, prettyT, set, substitute, symbolFunction, trie, TYPE_VARS, UnifyError } from "dope2";
|
||||||
|
|
||||||
import type { EditorState } from "./Editor";
|
import type { EditorState } from "./Editor";
|
||||||
import type { InputValueType, SuggestionType } from "./InputBlock";
|
import type { InputValueType, SuggestionType } from "./InputBlock";
|
||||||
|
|
@ -30,7 +30,11 @@ export interface Unknown {
|
||||||
substitutions: Map<Type,Type>;
|
substitutions: Map<Type,Type>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const entirelyUnknown: Unknown = { kind: "unknown", t: makeGeneric(a => a), substitutions: new Map() };
|
export const entirelyUnknown = env => ({
|
||||||
|
kind: "unknown",
|
||||||
|
t: getUnusedTypeVar(env),
|
||||||
|
substitutions: new Map(),
|
||||||
|
} as Unknown);
|
||||||
|
|
||||||
// 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;
|
||||||
|
|
@ -40,9 +44,7 @@ export const evalEditorBlock = (s: EditorState, env): ResolvedType => {
|
||||||
return evalInputBlock(s.text, s.value, env);
|
return evalInputBlock(s.text, s.value, env);
|
||||||
}
|
}
|
||||||
if (s.kind === "call") {
|
if (s.kind === "call") {
|
||||||
const fn = evalEditorBlock(s.fn, env);
|
return evalCallBlock(s.fn, s.input, env);
|
||||||
const input = evalEditorBlock(s.input, env);
|
|
||||||
return evalCallBlock(fn, input);
|
|
||||||
}
|
}
|
||||||
if (s.kind === "let") {
|
if (s.kind === "let") {
|
||||||
return evalLetInBlock(s.value, s.name, s.inner, env);
|
return evalLetInBlock(s.value, s.name, s.inner, env);
|
||||||
|
|
@ -51,12 +53,12 @@ export const evalEditorBlock = (s: EditorState, env): ResolvedType => {
|
||||||
return evalLambdaBlock(s.paramName, s.expr, env);
|
return evalLambdaBlock(s.paramName, s.expr, env);
|
||||||
|
|
||||||
}
|
}
|
||||||
return entirelyUnknown; // todo
|
return entirelyUnknown(env); // todo
|
||||||
};
|
};
|
||||||
|
|
||||||
export function evalInputBlock(text: string, value: InputValueType, env): ResolvedType {
|
export function evalInputBlock(text: string, value: InputValueType, env): ResolvedType {
|
||||||
if (value.kind === "literal") {
|
if (value.kind === "literal") {
|
||||||
return parseLiteral(text, value.type);
|
return parseLiteral(text, value.type, env);
|
||||||
}
|
}
|
||||||
else if (value.kind === "name") {
|
else if (value.kind === "name") {
|
||||||
const found = trie.get(env.name2dyn)(text);
|
const found = trie.get(env.name2dyn)(text);
|
||||||
|
|
@ -67,11 +69,11 @@ export function evalInputBlock(text: string, value: InputValueType, env): Resolv
|
||||||
substitutions: new Map(),
|
substitutions: new Map(),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return entirelyUnknown;
|
return entirelyUnknown(env);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { // kind === "text" -> unresolved
|
else { // kind === "text" -> unresolved
|
||||||
return entirelyUnknown;
|
return entirelyUnknown(env);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,48 +81,48 @@ const mergeMaps = (...maps: Map<Type,Type>[]) => {
|
||||||
return new Map(maps.flatMap(m => [...m]));
|
return new Map(maps.flatMap(m => [...m]));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function evalCallBlock(fn: ResolvedType, input: ResolvedType): ResolvedType {
|
export function evalCallBlock2(fnResolved: ResolvedType, inputResolved: ResolvedType, env): ResolvedType {
|
||||||
if (getSymbol(fn.t) !== symbolFunction) {
|
if (getSymbol(fnResolved.t) !== symbolFunction) {
|
||||||
if (fn.kind === "unknown") {
|
if (fnResolved.kind === "unknown") {
|
||||||
return entirelyUnknown; // don't flash everything red, giving the user a heart attack
|
return entirelyUnknown(env); // don't flash everything red, giving the user a heart attack
|
||||||
}
|
}
|
||||||
// worst outcome: we know nothing about the result!
|
// worst outcome: we know nothing about the result!
|
||||||
return {
|
return {
|
||||||
kind: "error",
|
kind: "error",
|
||||||
e: new NotAFunctionError(`${prettyT(fn.t)} is not a function type!`),
|
e: new NotAFunctionError(`${prettyT(fnResolved.t)} is not a function type!`),
|
||||||
t: entirelyUnknown.t,
|
t: getUnusedTypeVar(env),
|
||||||
substitutions: mergeMaps(fn.substitutions, input.substitutions),
|
substitutions: mergeMaps(fnResolved.substitutions, inputResolved.substitutions),
|
||||||
depth: 0,
|
depth: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// fn is a function...
|
// fn is a function...
|
||||||
const [outType, substitutions] = assignFnSubstitutions(fn.t, input.t); // may throw
|
const [outType, substitutions] = assignFnSubstitutions(fnResolved.t, inputResolved.t); // may throw
|
||||||
|
|
||||||
const mergedSubstitutions = mergeMaps(substitutions, fn.substitutions, input.substitutions);
|
const mergedSubstitutions = mergeMaps(substitutions, fnResolved.substitutions, inputResolved.substitutions);
|
||||||
|
|
||||||
if (input.kind === "error") {
|
if (inputResolved.kind === "error") {
|
||||||
return {
|
return {
|
||||||
kind: "error",
|
kind: "error",
|
||||||
e: input.e, // bubble up the error
|
e: inputResolved.e, // bubble up the error
|
||||||
depth: 0,
|
depth: 0,
|
||||||
t: outType,
|
t: outType,
|
||||||
substitutions: mergedSubstitutions,
|
substitutions: mergedSubstitutions,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (fn.kind === "error") {
|
if (fnResolved.kind === "error") {
|
||||||
// also bubble up
|
// also bubble up
|
||||||
return {
|
return {
|
||||||
kind: "error",
|
kind: "error",
|
||||||
e: fn.e,
|
e: fnResolved.e,
|
||||||
depth: fn.depth+1,
|
depth: fnResolved.depth+1,
|
||||||
t: outType,
|
t: outType,
|
||||||
substitutions: mergedSubstitutions,
|
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 (inputResolved.kind === "value" && fnResolved.kind === "value") {
|
||||||
const outValue = fn.i(input.i);
|
const outValue = fnResolved.i(inputResolved.i);
|
||||||
return {
|
return {
|
||||||
kind: "value",
|
kind: "value",
|
||||||
i: outValue,
|
i: outValue,
|
||||||
|
|
@ -140,31 +142,46 @@ export function evalCallBlock(fn: ResolvedType, input: ResolvedType): ResolvedTy
|
||||||
catch (e) {
|
catch (e) {
|
||||||
if ((e instanceof UnifyError)) {
|
if ((e instanceof UnifyError)) {
|
||||||
// even though fn was incompatible with the given parameter, we can still suppose that our output-type will be that of fn...?
|
// even though fn was incompatible with the given parameter, we can still suppose that our output-type will be that of fn...?
|
||||||
const outType = fn.t.params[1](fn.t);
|
const outType = fnResolved.t.params[1](fnResolved.t);
|
||||||
return {
|
return {
|
||||||
kind: "error",
|
kind: "error",
|
||||||
e,
|
e,
|
||||||
depth: 0,
|
depth: 0,
|
||||||
t: outType,
|
t: outType,
|
||||||
substitutions: mergeMaps(fn.substitutions, input.substitutions),
|
substitutions: mergeMaps(fnResolved.substitutions, inputResolved.substitutions),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function evalCallBlock(fn: EditorState, input: EditorState, env): ResolvedType {
|
||||||
|
const fnResolved = evalEditorBlock(fn, env);
|
||||||
|
const inputResolved = evalEditorBlock(input, env);
|
||||||
|
return evalCallBlock2(fnResolved, inputResolved, env);
|
||||||
|
}
|
||||||
|
|
||||||
export function evalLetInBlock(value: EditorState, name: string, inner: EditorState, env): ResolvedType {
|
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 getUnusedTypeVar(env) {
|
||||||
|
for (let i=0; ; i++) {
|
||||||
|
if (!dict.has(env.typeDict)(TYPE_VARS[i])) {
|
||||||
|
return TYPE_VARS[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function evalLambdaBlock(paramName: string, expr: EditorState, env): ResolvedType {
|
export function evalLambdaBlock(paramName: string, expr: EditorState, env): ResolvedType {
|
||||||
|
const paramType = getUnusedTypeVar(env);
|
||||||
const fn = (x: any) => {
|
const fn = (x: any) => {
|
||||||
const innerEnv = makeInnerEnv(env, paramName, {
|
const innerEnv = makeInnerEnv(env, paramName, {
|
||||||
kind: "value",
|
kind: "value",
|
||||||
i: x,
|
i: x,
|
||||||
t: TYPE_VARS[0],
|
t: paramType,
|
||||||
substitutions: new Map(),
|
substitutions: new Map(),
|
||||||
});
|
});
|
||||||
const result = evalEditorBlock(expr, innerEnv);
|
const result = evalEditorBlock(expr, innerEnv);
|
||||||
|
|
@ -175,11 +192,11 @@ export function evalLambdaBlock(paramName: string, expr: EditorState, env): Reso
|
||||||
// static env: we only know the name and the type
|
// static env: we only know the name and the type
|
||||||
const staticInnerEnv = makeInnerEnv(env, paramName, {
|
const staticInnerEnv = makeInnerEnv(env, paramName, {
|
||||||
kind: "unknown", // parameter value is not statically known
|
kind: "unknown", // parameter value is not statically known
|
||||||
t: TYPE_VARS[0],
|
t: paramType,
|
||||||
substitutions: new Map(),
|
substitutions: new Map(),
|
||||||
});
|
});
|
||||||
const abstractOutput = evalEditorBlock(expr, staticInnerEnv);
|
const abstractOutput = evalEditorBlock(expr, staticInnerEnv);
|
||||||
const t = fnType(_ => entirelyUnknown.t)(_ => abstractOutput.t);
|
const t = fnType(_ => paramType)(_ => abstractOutput.t);
|
||||||
const T = substitute(t, abstractOutput.substitutions, [])
|
const T = substitute(t, abstractOutput.substitutions, [])
|
||||||
return {
|
return {
|
||||||
kind: "value",
|
kind: "value",
|
||||||
|
|
@ -194,18 +211,18 @@ export function haveValue(resolved: ResolvedType) {
|
||||||
return resolved.kind === "value";
|
return resolved.kind === "value";
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseLiteral(text: string, type: string): ResolvedType {
|
function parseLiteral(text: string, type: string, env): ResolvedType {
|
||||||
// dirty
|
// dirty
|
||||||
if (type === "Int") {
|
if (type === "Int") {
|
||||||
return parseAsInt(text);
|
return parseAsInt(text, env);
|
||||||
}
|
}
|
||||||
if (type === "Double") {
|
if (type === "Double") {
|
||||||
return parseAsDouble(text);
|
return parseAsDouble(text, env);
|
||||||
}
|
}
|
||||||
return entirelyUnknown;
|
return entirelyUnknown(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseAsDouble(text: string): ResolvedType {
|
function parseAsDouble(text: string, env): ResolvedType {
|
||||||
if (text !== '') {
|
if (text !== '') {
|
||||||
const num = Number(text);
|
const num = Number(text);
|
||||||
if (!Number.isNaN(num)) {
|
if (!Number.isNaN(num)) {
|
||||||
|
|
@ -217,9 +234,9 @@ function parseAsDouble(text: string): ResolvedType {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return entirelyUnknown;
|
return entirelyUnknown(env);
|
||||||
}
|
}
|
||||||
function parseAsInt(text: string): ResolvedType {
|
function parseAsInt(text: string, env): ResolvedType {
|
||||||
if (text !== '') {
|
if (text !== '') {
|
||||||
try {
|
try {
|
||||||
return {
|
return {
|
||||||
|
|
@ -231,13 +248,13 @@ function parseAsInt(text: string): ResolvedType {
|
||||||
}
|
}
|
||||||
catch {}
|
catch {}
|
||||||
}
|
}
|
||||||
return entirelyUnknown;
|
return entirelyUnknown(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
const literalParsers = [parseAsDouble, parseAsInt];
|
const literalParsers = [parseAsDouble, parseAsInt];
|
||||||
|
|
||||||
export function attemptParseLiteral(text: string): Dynamic[] {
|
export function attemptParseLiteral(text: string, env): Dynamic[] {
|
||||||
return literalParsers.map(parseFn => parseFn(text))
|
return literalParsers.map(parseFn => parseFn(text, env))
|
||||||
.filter(resolved => (resolved.kind !== "unknown" && resolved.kind !== "error")) as unknown as Dynamic[];
|
.filter(resolved => (resolved.kind !== "unknown" && resolved.kind !== "error")) as unknown as Dynamic[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue