greatly simplify and improve type inferencing - suggestions broken
This commit is contained in:
parent
c3f7cea310
commit
c5c5def598
12 changed files with 1022 additions and 694 deletions
|
|
@ -18,9 +18,9 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.27.0",
|
||||
"@types/react": "^19.1.4",
|
||||
"@types/react": "^19.1.5",
|
||||
"@types/react-dom": "^19.1.5",
|
||||
"@vitejs/plugin-react-swc": "^3.9.0",
|
||||
"@vitejs/plugin-react-swc": "^3.10.0",
|
||||
"eslint": "^9.27.0",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.20",
|
||||
|
|
|
|||
128
pnpm-lock.yaml
generated
128
pnpm-lock.yaml
generated
|
|
@ -16,7 +16,7 @@ importers:
|
|||
version: 5.2.5
|
||||
dope2:
|
||||
specifier: git+https://deemz.org/git/joeri/dope2.git
|
||||
version: git+https://deemz.org/git/joeri/dope2.git#8cfbd6116ffe778efb02c37133a1ff633ae171df
|
||||
version: git+https://deemz.org/git/joeri/dope2.git#0d3ccee7d5ce703e71fd3c05e1795c7968646a34
|
||||
react:
|
||||
specifier: ^19.1.0
|
||||
version: 19.1.0
|
||||
|
|
@ -28,14 +28,14 @@ importers:
|
|||
specifier: ^9.27.0
|
||||
version: 9.27.0
|
||||
'@types/react':
|
||||
specifier: ^19.1.4
|
||||
version: 19.1.4
|
||||
specifier: ^19.1.5
|
||||
version: 19.1.5
|
||||
'@types/react-dom':
|
||||
specifier: ^19.1.5
|
||||
version: 19.1.5(@types/react@19.1.4)
|
||||
version: 19.1.5(@types/react@19.1.5)
|
||||
'@vitejs/plugin-react-swc':
|
||||
specifier: ^3.9.0
|
||||
version: 3.9.0(vite@6.3.5)
|
||||
specifier: ^3.10.0
|
||||
version: 3.10.0(vite@6.3.5)
|
||||
eslint:
|
||||
specifier: ^9.27.0
|
||||
version: 9.27.0
|
||||
|
|
@ -286,6 +286,9 @@ packages:
|
|||
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
'@rolldown/pluginutils@1.0.0-beta.9':
|
||||
resolution: {integrity: sha512-e9MeMtVWo186sgvFFJOPGy7/d2j2mZhLJIdVW0C/xDluuOvymEATqz6zKsP0ZmXGzQtqlyjz5sC1sYQUoJG98w==}
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.41.0':
|
||||
resolution: {integrity: sha512-KxN+zCjOYHGwCl4UCtSfZ6jrq/qi88JDUtiEFk8LELEHq2Egfc/FgW+jItZiOLRuQfb/3xJSgFuNPC9jzggX+A==}
|
||||
cpu: [arm]
|
||||
|
|
@ -386,68 +389,68 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@swc/core-darwin-arm64@1.11.24':
|
||||
resolution: {integrity: sha512-dhtVj0PC1APOF4fl5qT2neGjRLgHAAYfiVP8poJelhzhB/318bO+QCFWAiimcDoyMgpCXOhTp757gnoJJrheWA==}
|
||||
'@swc/core-darwin-arm64@1.11.29':
|
||||
resolution: {integrity: sha512-whsCX7URzbuS5aET58c75Dloby3Gtj/ITk2vc4WW6pSDQKSPDuONsIcZ7B2ng8oz0K6ttbi4p3H/PNPQLJ4maQ==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@swc/core-darwin-x64@1.11.24':
|
||||
resolution: {integrity: sha512-H/3cPs8uxcj2Fe3SoLlofN5JG6Ny5bl8DuZ6Yc2wr7gQFBmyBkbZEz+sPVgsID7IXuz7vTP95kMm1VL74SO5AQ==}
|
||||
'@swc/core-darwin-x64@1.11.29':
|
||||
resolution: {integrity: sha512-S3eTo/KYFk+76cWJRgX30hylN5XkSmjYtCBnM4jPLYn7L6zWYEPajsFLmruQEiTEDUg0gBEWLMNyUeghtswouw==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@swc/core-linux-arm-gnueabihf@1.11.24':
|
||||
resolution: {integrity: sha512-PHJgWEpCsLo/NGj+A2lXZ2mgGjsr96ULNW3+T3Bj2KTc8XtMUkE8tmY2Da20ItZOvPNC/69KroU7edyo1Flfbw==}
|
||||
'@swc/core-linux-arm-gnueabihf@1.11.29':
|
||||
resolution: {integrity: sha512-o9gdshbzkUMG6azldHdmKklcfrcMx+a23d/2qHQHPDLUPAN+Trd+sDQUYArK5Fcm7TlpG4sczz95ghN0DMkM7g==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@swc/core-linux-arm64-gnu@1.11.24':
|
||||
resolution: {integrity: sha512-C2FJb08+n5SD4CYWCTZx1uR88BN41ZieoHvI8A55hfVf2woT8+6ZiBzt74qW2g+ntZ535Jts5VwXAKdu41HpBg==}
|
||||
'@swc/core-linux-arm64-gnu@1.11.29':
|
||||
resolution: {integrity: sha512-sLoaciOgUKQF1KX9T6hPGzvhOQaJn+3DHy4LOHeXhQqvBgr+7QcZ+hl4uixPKTzxk6hy6Hb0QOvQEdBAAR1gXw==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@swc/core-linux-arm64-musl@1.11.24':
|
||||
resolution: {integrity: sha512-ypXLIdszRo0re7PNNaXN0+2lD454G8l9LPK/rbfRXnhLWDBPURxzKlLlU/YGd2zP98wPcVooMmegRSNOKfvErw==}
|
||||
'@swc/core-linux-arm64-musl@1.11.29':
|
||||
resolution: {integrity: sha512-PwjB10BC0N+Ce7RU/L23eYch6lXFHz7r3NFavIcwDNa/AAqywfxyxh13OeRy+P0cg7NDpWEETWspXeI4Ek8otw==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@swc/core-linux-x64-gnu@1.11.24':
|
||||
resolution: {integrity: sha512-IM7d+STVZD48zxcgo69L0yYptfhaaE9cMZ+9OoMxirNafhKKXwoZuufol1+alEFKc+Wbwp+aUPe/DeWC/Lh3dg==}
|
||||
'@swc/core-linux-x64-gnu@1.11.29':
|
||||
resolution: {integrity: sha512-i62vBVoPaVe9A3mc6gJG07n0/e7FVeAvdD9uzZTtGLiuIfVfIBta8EMquzvf+POLycSk79Z6lRhGPZPJPYiQaA==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@swc/core-linux-x64-musl@1.11.24':
|
||||
resolution: {integrity: sha512-DZByJaMVzSfjQKKQn3cqSeqwy6lpMaQDQQ4HPlch9FWtDx/dLcpdIhxssqZXcR2rhaQVIaRQsCqwV6orSDGAGw==}
|
||||
'@swc/core-linux-x64-musl@1.11.29':
|
||||
resolution: {integrity: sha512-YER0XU1xqFdK0hKkfSVX1YIyCvMDI7K07GIpefPvcfyNGs38AXKhb2byySDjbVxkdl4dycaxxhRyhQ2gKSlsFQ==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@swc/core-win32-arm64-msvc@1.11.24':
|
||||
resolution: {integrity: sha512-Q64Ytn23y9aVDKN5iryFi8mRgyHw3/kyjTjT4qFCa8AEb5sGUuSj//AUZ6c0J7hQKMHlg9do5Etvoe61V98/JQ==}
|
||||
'@swc/core-win32-arm64-msvc@1.11.29':
|
||||
resolution: {integrity: sha512-po+WHw+k9g6FAg5IJ+sMwtA/fIUL3zPQ4m/uJgONBATCVnDDkyW6dBA49uHNVtSEvjvhuD8DVWdFP847YTcITw==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@swc/core-win32-ia32-msvc@1.11.24':
|
||||
resolution: {integrity: sha512-9pKLIisE/Hh2vJhGIPvSoTK4uBSPxNVyXHmOrtdDot4E1FUUI74Vi8tFdlwNbaj8/vusVnb8xPXsxF1uB0VgiQ==}
|
||||
'@swc/core-win32-ia32-msvc@1.11.29':
|
||||
resolution: {integrity: sha512-h+NjOrbqdRBYr5ItmStmQt6x3tnhqgwbj9YxdGPepbTDamFv7vFnhZR0YfB3jz3UKJ8H3uGJ65Zw1VsC+xpFkg==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@swc/core-win32-x64-msvc@1.11.24':
|
||||
resolution: {integrity: sha512-sybnXtOsdB+XvzVFlBVGgRHLqp3yRpHK7CrmpuDKszhj/QhmsaZzY/GHSeALlMtLup13M0gqbcQvsTNlAHTg3w==}
|
||||
'@swc/core-win32-x64-msvc@1.11.29':
|
||||
resolution: {integrity: sha512-Q8cs2BDV9wqDvqobkXOYdC+pLUSEpX/KvI0Dgfun1F+LzuLotRFuDhrvkU9ETJA6OnD2+Fn/ieHgloiKA/Mn/g==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@swc/core@1.11.24':
|
||||
resolution: {integrity: sha512-MaQEIpfcEMzx3VWWopbofKJvaraqmL6HbLlw2bFZ7qYqYw3rkhM0cQVEgyzbHtTWwCwPMFZSC2DUbhlZgrMfLg==}
|
||||
'@swc/core@1.11.29':
|
||||
resolution: {integrity: sha512-g4mThMIpWbNhV8G2rWp5a5/Igv8/2UFRJx2yImrLGMgrDDYZIopqZ/z0jZxDgqNA1QDx93rpwNF7jGsxVWcMlA==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
'@swc/helpers': '>=0.5.17'
|
||||
|
|
@ -472,8 +475,8 @@ packages:
|
|||
peerDependencies:
|
||||
'@types/react': ^19.0.0
|
||||
|
||||
'@types/react@19.1.4':
|
||||
resolution: {integrity: sha512-EB1yiiYdvySuIITtD5lhW4yPyJ31RkJkkDw794LaQYrxCSaQV/47y5o1FMC4zF9ZyjUjzJMZwbovEnT5yHTW6g==}
|
||||
'@types/react@19.1.5':
|
||||
resolution: {integrity: sha512-piErsCVVbpMMT2r7wbawdZsq4xMvIAhQuac2gedQHysu1TZYEigE6pnFfgZT+/jQnrRuF5r+SHzuehFjfRjr4g==}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.32.1':
|
||||
resolution: {integrity: sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==}
|
||||
|
|
@ -522,8 +525,8 @@ packages:
|
|||
resolution: {integrity: sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@vitejs/plugin-react-swc@3.9.0':
|
||||
resolution: {integrity: sha512-jYFUSXhwMCYsh/aQTgSGLIN3Foz5wMbH9ahb0Zva//UzwZYbMiZd7oT3AU9jHT9DLswYDswsRwPU9jVF3yA48Q==}
|
||||
'@vitejs/plugin-react-swc@3.10.0':
|
||||
resolution: {integrity: sha512-ZmkdHw3wo/o/Rk05YsXZs/DJAfY2CdQ5DUAjoWji+PEr+hYADdGMCGgEAILbiKj+CjspBTuTACBcWDrmC8AUfw==}
|
||||
peerDependencies:
|
||||
vite: ^4 || ^5 || ^6
|
||||
|
||||
|
|
@ -597,8 +600,8 @@ packages:
|
|||
deep-is@0.1.4:
|
||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||
|
||||
dope2@git+https://deemz.org/git/joeri/dope2.git#8cfbd6116ffe778efb02c37133a1ff633ae171df:
|
||||
resolution: {commit: 8cfbd6116ffe778efb02c37133a1ff633ae171df, repo: https://deemz.org/git/joeri/dope2.git, type: git}
|
||||
dope2@git+https://deemz.org/git/joeri/dope2.git#0d3ccee7d5ce703e71fd3c05e1795c7968646a34:
|
||||
resolution: {commit: 0d3ccee7d5ce703e71fd3c05e1795c7968646a34, repo: https://deemz.org/git/joeri/dope2.git, type: git}
|
||||
version: 0.0.1
|
||||
|
||||
esbuild@0.25.4:
|
||||
|
|
@ -1160,6 +1163,8 @@ snapshots:
|
|||
'@nodelib/fs.scandir': 2.1.5
|
||||
fastq: 1.19.1
|
||||
|
||||
'@rolldown/pluginutils@1.0.0-beta.9': {}
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.41.0':
|
||||
optional: true
|
||||
|
||||
|
|
@ -1220,51 +1225,51 @@ snapshots:
|
|||
'@rollup/rollup-win32-x64-msvc@4.41.0':
|
||||
optional: true
|
||||
|
||||
'@swc/core-darwin-arm64@1.11.24':
|
||||
'@swc/core-darwin-arm64@1.11.29':
|
||||
optional: true
|
||||
|
||||
'@swc/core-darwin-x64@1.11.24':
|
||||
'@swc/core-darwin-x64@1.11.29':
|
||||
optional: true
|
||||
|
||||
'@swc/core-linux-arm-gnueabihf@1.11.24':
|
||||
'@swc/core-linux-arm-gnueabihf@1.11.29':
|
||||
optional: true
|
||||
|
||||
'@swc/core-linux-arm64-gnu@1.11.24':
|
||||
'@swc/core-linux-arm64-gnu@1.11.29':
|
||||
optional: true
|
||||
|
||||
'@swc/core-linux-arm64-musl@1.11.24':
|
||||
'@swc/core-linux-arm64-musl@1.11.29':
|
||||
optional: true
|
||||
|
||||
'@swc/core-linux-x64-gnu@1.11.24':
|
||||
'@swc/core-linux-x64-gnu@1.11.29':
|
||||
optional: true
|
||||
|
||||
'@swc/core-linux-x64-musl@1.11.24':
|
||||
'@swc/core-linux-x64-musl@1.11.29':
|
||||
optional: true
|
||||
|
||||
'@swc/core-win32-arm64-msvc@1.11.24':
|
||||
'@swc/core-win32-arm64-msvc@1.11.29':
|
||||
optional: true
|
||||
|
||||
'@swc/core-win32-ia32-msvc@1.11.24':
|
||||
'@swc/core-win32-ia32-msvc@1.11.29':
|
||||
optional: true
|
||||
|
||||
'@swc/core-win32-x64-msvc@1.11.24':
|
||||
'@swc/core-win32-x64-msvc@1.11.29':
|
||||
optional: true
|
||||
|
||||
'@swc/core@1.11.24':
|
||||
'@swc/core@1.11.29':
|
||||
dependencies:
|
||||
'@swc/counter': 0.1.3
|
||||
'@swc/types': 0.1.21
|
||||
optionalDependencies:
|
||||
'@swc/core-darwin-arm64': 1.11.24
|
||||
'@swc/core-darwin-x64': 1.11.24
|
||||
'@swc/core-linux-arm-gnueabihf': 1.11.24
|
||||
'@swc/core-linux-arm64-gnu': 1.11.24
|
||||
'@swc/core-linux-arm64-musl': 1.11.24
|
||||
'@swc/core-linux-x64-gnu': 1.11.24
|
||||
'@swc/core-linux-x64-musl': 1.11.24
|
||||
'@swc/core-win32-arm64-msvc': 1.11.24
|
||||
'@swc/core-win32-ia32-msvc': 1.11.24
|
||||
'@swc/core-win32-x64-msvc': 1.11.24
|
||||
'@swc/core-darwin-arm64': 1.11.29
|
||||
'@swc/core-darwin-x64': 1.11.29
|
||||
'@swc/core-linux-arm-gnueabihf': 1.11.29
|
||||
'@swc/core-linux-arm64-gnu': 1.11.29
|
||||
'@swc/core-linux-arm64-musl': 1.11.29
|
||||
'@swc/core-linux-x64-gnu': 1.11.29
|
||||
'@swc/core-linux-x64-musl': 1.11.29
|
||||
'@swc/core-win32-arm64-msvc': 1.11.29
|
||||
'@swc/core-win32-ia32-msvc': 1.11.29
|
||||
'@swc/core-win32-x64-msvc': 1.11.29
|
||||
|
||||
'@swc/counter@0.1.3': {}
|
||||
|
||||
|
|
@ -1276,11 +1281,11 @@ snapshots:
|
|||
|
||||
'@types/json-schema@7.0.15': {}
|
||||
|
||||
'@types/react-dom@19.1.5(@types/react@19.1.4)':
|
||||
'@types/react-dom@19.1.5(@types/react@19.1.5)':
|
||||
dependencies:
|
||||
'@types/react': 19.1.4
|
||||
'@types/react': 19.1.5
|
||||
|
||||
'@types/react@19.1.4':
|
||||
'@types/react@19.1.5':
|
||||
dependencies:
|
||||
csstype: 3.1.3
|
||||
|
||||
|
|
@ -1361,9 +1366,10 @@ snapshots:
|
|||
'@typescript-eslint/types': 8.32.1
|
||||
eslint-visitor-keys: 4.2.0
|
||||
|
||||
'@vitejs/plugin-react-swc@3.9.0(vite@6.3.5)':
|
||||
'@vitejs/plugin-react-swc@3.10.0(vite@6.3.5)':
|
||||
dependencies:
|
||||
'@swc/core': 1.11.24
|
||||
'@rolldown/pluginutils': 1.0.0-beta.9
|
||||
'@swc/core': 1.11.29
|
||||
vite: 6.3.5
|
||||
transitivePeerDependencies:
|
||||
- '@swc/helpers'
|
||||
|
|
@ -1431,7 +1437,7 @@ snapshots:
|
|||
|
||||
deep-is@0.1.4: {}
|
||||
|
||||
dope2@git+https://deemz.org/git/joeri/dope2.git#8cfbd6116ffe778efb02c37133a1ff633ae171df:
|
||||
dope2@git+https://deemz.org/git/joeri/dope2.git#0d3ccee7d5ce703e71fd3c05e1795c7968646a34:
|
||||
dependencies:
|
||||
functional-red-black-tree: 1.0.1
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { ExprBlock, type ExprBlockState } from './ExprBlock';
|
|||
import { GlobalContext } from './GlobalContext';
|
||||
import { biggerExample, emptySet, factorial, higherOrder, higherOrder2Params, inc, initialEditorState, lambda2Params, nonEmptyEditorState, pushBool, tripleFunctionCallEditorState } from "./configurations";
|
||||
import { actionShortcuts } from './actions';
|
||||
import { scoreResolved, type ResolvedType } from './eval';
|
||||
// import { scoreResolved, type ResolvedType } from './eval';
|
||||
|
||||
|
||||
const examples: [string, ExprBlockState][] = [
|
||||
|
|
@ -62,6 +62,8 @@ export function App() {
|
|||
setAppState(_ => defaultState);
|
||||
}
|
||||
|
||||
// factoryReset();
|
||||
|
||||
const pushHistory = (callback: (p: ExprBlockState) => ExprBlockState) => {
|
||||
setAppState(({history}) => {
|
||||
const newState = callback(history.at(-1)!);
|
||||
|
|
@ -166,9 +168,7 @@ export function App() {
|
|||
state={appState.history.at(-1)!}
|
||||
setState={pushHistory}
|
||||
onCancel={() => {}}
|
||||
score={(resolved: ResolvedType) => {
|
||||
return scoreResolved(resolved, () => 0);
|
||||
}}
|
||||
score={() => 0}
|
||||
/>
|
||||
</GlobalContext>
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useContext } from "react";
|
||||
|
||||
import { EnvContext } from "./EnvContext";
|
||||
import { addFocusRightMost, evalCallBlock2, evalExprBlock, recomputeTypeVarsForEnv, scoreResolved, type Environment, type ResolvedType } from "./eval";
|
||||
// import { addFocusRightMost, evalCallBlock2, evalExprBlock, recomputeTypeVarsForEnv, scoreResolved, type Environment, type ResolvedType } from "./eval";
|
||||
import { ExprBlock, type ExprBlockState, type SetStateFn, type State2Props } from "./ExprBlock";
|
||||
import { GlobalContext } from "./GlobalContext";
|
||||
import { Value } from "./Value";
|
||||
|
|
@ -9,6 +9,8 @@ import { Value } from "./Value";
|
|||
import { getActions } from "./actions";
|
||||
import "./CallBlock.css";
|
||||
import { CallContext } from "./CallContext";
|
||||
import { inferType, inferTypeCall, type Environment } from "./infer_type";
|
||||
import { Type } from "./Type";
|
||||
|
||||
export interface CallBlockState {
|
||||
kind: "call";
|
||||
|
|
@ -21,20 +23,19 @@ export interface CallBlockProps<
|
|||
InputState=ExprBlockState,
|
||||
> extends State2Props<CallBlockState,ExprBlockState> {}
|
||||
|
||||
function nestedFnProperties({state, setState, score}: CallBlockProps, env) {
|
||||
function nestedFnProperties({state, setState, score}: CallBlockProps, env: Environment) {
|
||||
const setFn = (callback: SetStateFn) => {
|
||||
setState(state => ({...state, fn: callback(state.fn)}));
|
||||
}
|
||||
};
|
||||
const onFnCancel = () => {
|
||||
setState(state => state.input); // we become our input
|
||||
}
|
||||
const scoreFn = (fnSuggestion: ResolvedType) => {
|
||||
return computePriority(
|
||||
fnSuggestion,
|
||||
evalExprBlock(state.input, env)[0],
|
||||
score,
|
||||
env,
|
||||
);
|
||||
};
|
||||
const scoreFn = (fnSuggestion: ExprBlockState) => {
|
||||
return score({
|
||||
kind: "call",
|
||||
fn: fnSuggestion,
|
||||
input: state.input,
|
||||
});
|
||||
};
|
||||
return {state: state.fn, setState: setFn, onCancel: onFnCancel, score: scoreFn};
|
||||
}
|
||||
|
|
@ -42,36 +43,37 @@ function nestedFnProperties({state, setState, score}: CallBlockProps, env) {
|
|||
function nestedInputProperties({state, setState, score}: CallBlockProps, env: Environment) {
|
||||
const setInput = (callback: SetStateFn) => {
|
||||
setState(state => ({...state, input: callback(state.input)}));
|
||||
}
|
||||
};
|
||||
const onInputCancel = () => {
|
||||
setState(state => addFocusRightMost(state.fn)); // we become our function
|
||||
}
|
||||
const scoreInput = (inputSuggestion: ResolvedType) => {
|
||||
return computePriority(
|
||||
evalExprBlock(state.fn, env)[0], // fn *may* be set
|
||||
inputSuggestion, // suggestions will be for input
|
||||
score, // priority function we get from parent block
|
||||
env,
|
||||
);
|
||||
}
|
||||
setState(state => /*addFocusRightMost*/(state.fn)); // we become our function
|
||||
};
|
||||
const scoreInput = (inputSuggestion: ExprBlockState) => {
|
||||
return score({
|
||||
kind: "call",
|
||||
fn: state.fn,
|
||||
input: inputSuggestion,
|
||||
});
|
||||
};
|
||||
return {state: state.input, setState: setInput, onCancel: onInputCancel, score: scoreInput};
|
||||
}
|
||||
|
||||
function computePriority(fn: ResolvedType, input: ResolvedType, outPriority: (s: ResolvedType) => number, env) {
|
||||
// dirty, but works:
|
||||
const [fnR, env2] = recomputeTypeVarsForEnv('<fn>', fn, env);
|
||||
const [inR, env3] = recomputeTypeVarsForEnv('<in>', input, env2);
|
||||
const [resolved] = evalCallBlock2(fnR, inR, env3);
|
||||
const score = scoreResolved(resolved, outPriority);
|
||||
return score;
|
||||
}
|
||||
// function computePriority(fn: ResolvedType, input: ResolvedType, outPriority: (s: ResolvedType) => number, env) {
|
||||
// // dirty, but works:
|
||||
// const [fnR, env2] = recomputeTypeVarsForEnv('<fn>', fn, env);
|
||||
// const [inR, env3] = recomputeTypeVarsForEnv('<in>', input, env2);
|
||||
// const [resolved] = evalCallBlock2(fnR, inR, env3);
|
||||
// const score = scoreResolved(resolved, outPriority);
|
||||
// return score;
|
||||
// }
|
||||
|
||||
export function CallBlock(props: CallBlockProps) {
|
||||
const env = useContext(EnvContext);
|
||||
const globalContext = useContext(GlobalContext);
|
||||
const addParam = getActions(globalContext, props.setState).c;
|
||||
const [resolved] = evalExprBlock(props.state, env);
|
||||
return <span className={"functionBlock" + ((resolved.kind === "error") ? " unifyError" : "")}>
|
||||
// const [resolved] = evalExprBlock(props.state, env);
|
||||
// return <span className={"functionBlock" + ((resolved.kind === "error") ? " unifyError" : "")}>
|
||||
const typeInfo = inferTypeCall(props.state, env);
|
||||
return <span className={"functionBlock"}>
|
||||
<CallContext value={{addParam}}>
|
||||
<FunctionHeader {...props} addParam={addParam} />
|
||||
<div className="functionParams">
|
||||
|
|
@ -80,12 +82,14 @@ export function CallBlock(props: CallBlockProps) {
|
|||
<InputParams
|
||||
{...props}
|
||||
depth={0}
|
||||
errorDepth={(resolved.kind === "error") ? (resolved.depth) : -1}
|
||||
// errorDepth={(resolved.kind === "error") ? (resolved.depth) : -1}
|
||||
errorDepth={-1}
|
||||
addParam={addParam}
|
||||
/>
|
||||
{ (resolved.kind === "error") && resolved.e.toString()
|
||||
{/* { (resolved.kind === "error") && resolved.e.toString()
|
||||
|| (resolved.kind === "value") && <Value dynamic={resolved} />
|
||||
|| "unknown" }
|
||||
|| "unknown" } */}
|
||||
:: <Type type={typeInfo.type} />
|
||||
</div>
|
||||
</div>
|
||||
</CallContext>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { createContext } from "react";
|
||||
import { getDefaultTypeParser, module2Env, ModuleStd } from "dope2";
|
||||
import type { Dynamic, Environment } from "./eval";
|
||||
// import type { Dynamic, Environment } from "./eval";
|
||||
import type { Environment } from "./infer_type";
|
||||
|
||||
export const functionWith3Params = i => j => k => i+j+k;
|
||||
export const functionWith4Params = i => j => k => l => i+j+k+l;
|
||||
|
|
@ -14,10 +15,10 @@ export const extendedEnv: Environment = {
|
|||
["leqZero", {i: n => leq => otherwise => (n<=0)?leq({}):otherwise({}), t: mkType("Int -> (Unit -> a) -> (Unit -> a) -> a")}],
|
||||
["functionWith3Params", { i: functionWith3Params, t: mkType("Int->Int->Int->Int") }],
|
||||
["functionWith4Params", { i: functionWith4Params, t: mkType("Int->Int->Int->Int->Int")}]
|
||||
]).map(([name, {i,t}]) => [name, { kind: "value", i, t, unification: new Map() }] as [string, Dynamic])
|
||||
]).map(([name, {i,t}]) => [name, { kind: "value", i, t, unification: new Map() }] as [string, any])
|
||||
).name2dyn,
|
||||
nextFreeTypeVar: 0,
|
||||
typeVars: new Set(),
|
||||
// nextFreeTypeVar: 0,
|
||||
typevars: new Set(),
|
||||
};
|
||||
|
||||
export const EnvContext = createContext(extendedEnv);
|
||||
|
|
@ -9,11 +9,12 @@ import { InputBlock, type InputBlockProps, type InputBlockState } from "./InputB
|
|||
import { LambdaBlock, type LambdaBlockProps, type LambdaBlockState } from "./LambdaBlock";
|
||||
import { LetInBlock, type LetInBlockProps, type LetInBlockState } from "./LetInBlock";
|
||||
import { Type } from "./Type";
|
||||
import { evalExprBlock, type ResolvedType } from "./eval";
|
||||
// import { evalExprBlock, type ResolvedType } from "./eval";
|
||||
|
||||
import "./ExprBlock.css";
|
||||
import { Input } from "./Input";
|
||||
import { getActions } from "./actions";
|
||||
import { inferType } from "./infer_type";
|
||||
|
||||
export type ExprBlockState =
|
||||
InputBlockState
|
||||
|
|
@ -26,7 +27,7 @@ export type SetStateFn<InType = ExprBlockState, OutType = InType> = (state: InTy
|
|||
export interface State2Props<InType, OutType = InType> {
|
||||
state: InType;
|
||||
setState: (callback: SetStateFn<InType, OutType>) => void;
|
||||
score: (suggestion: ResolvedType) => number;
|
||||
score: (suggestion: ExprBlockState) => number;
|
||||
}
|
||||
|
||||
interface ExprBlockProps extends State2Props<ExprBlockState> {
|
||||
|
|
@ -44,19 +45,22 @@ export function ExprBlock(props: ExprBlockProps) {
|
|||
lambda: () => <LambdaBlock {...props as LambdaBlockProps} />,
|
||||
};
|
||||
|
||||
const [resolved] = evalExprBlock(props.state, env);
|
||||
// const [resolved] = evalExprBlock(props.state, env);
|
||||
// const typeInfo = inferType(props.state, env);
|
||||
const actions = getActions(globalContext, props.setState);
|
||||
const extraHandlers = Object.fromEntries(Object.entries(actions).map(([shortcut, action]) =>
|
||||
[shortcut, (e) => { e.preventDefault(); action(); }]))
|
||||
|
||||
return <span className={"editor" + ((resolved.kind!=="value") ? " "+resolved.kind : "")}>
|
||||
// return <span className={"editor" + ((resolved.kind!=="value") ? " "+resolved.kind : "")}>
|
||||
|
||||
return <span className={"editor"}>
|
||||
{renderBlock[props.state.kind]()}
|
||||
{/* @ts-ignore */}
|
||||
<div className={"typeSignature" + (resolved.__debug ? ' gotDebug' : '')}>
|
||||
:: <Type type={getType(resolved)} />
|
||||
{/* <div className={"typeSignature" + (resolved.__debug ? ' gotDebug' : '')}> */}
|
||||
{/* :: <Type type={typeInfo.type} /> */}
|
||||
{/* @ts-ignore */}
|
||||
{resolved.__debug && <div className="typeDebug">{resolved.__debug}</div>}
|
||||
</div>
|
||||
{/* {resolved.__debug && <div className="typeDebug">{resolved.__debug}</div>} */}
|
||||
{/* </div> */}
|
||||
<Input
|
||||
placeholder="<c>"
|
||||
text=""
|
||||
|
|
|
|||
|
|
@ -3,15 +3,16 @@ import { memo, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|||
import { getType, prettyT, trie } from "dope2";
|
||||
|
||||
import { EnvContext } from "./EnvContext";
|
||||
import type { Environment, ResolvedType } from "./eval";
|
||||
// import type { Environment, ResolvedType } from "./eval";
|
||||
import "./InputBlock.css";
|
||||
import { Type } from "./Type";
|
||||
import type { ExprBlockState, State2Props } from "./ExprBlock";
|
||||
import { attemptParseLiteral } from "./eval";
|
||||
// import { attemptParseLiteral } from "./eval";
|
||||
import { Input } from "./Input";
|
||||
import { CallContext } from "./CallContext";
|
||||
import { getActions } from "./actions";
|
||||
import { GlobalContext } from "./GlobalContext";
|
||||
import { inferTypeInput } from "./infer_type";
|
||||
|
||||
interface Literal {
|
||||
kind: "literal";
|
||||
|
|
@ -32,35 +33,35 @@ export interface InputBlockState {
|
|||
focus: boolean
|
||||
}
|
||||
|
||||
export type SuggestionType = ["literal"|"name", string, ResolvedType];
|
||||
export type PrioritizedSuggestionType = [number, ...SuggestionType];
|
||||
// export type SuggestionType = ["literal"|"name", string, ResolvedType];
|
||||
// export type PrioritizedSuggestionType = [number, ...SuggestionType];
|
||||
|
||||
export interface InputBlockProps extends State2Props<InputBlockState,ExprBlockState> {
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
const computeSuggestions = (
|
||||
text: string,
|
||||
env: Environment,
|
||||
score: InputBlockProps['score'],
|
||||
): PrioritizedSuggestionType[] => {
|
||||
const literals = attemptParseLiteral(text, env);
|
||||
const ls: SuggestionType[] = [
|
||||
// literals
|
||||
... literals.map((resolved) => ["literal", text, resolved]),
|
||||
// const computeSuggestions = (
|
||||
// text: string,
|
||||
// env: Environment,
|
||||
// score: InputBlockProps['score'],
|
||||
// ): PrioritizedSuggestionType[] => {
|
||||
// const literals = attemptParseLiteral(text, env);
|
||||
// const ls: SuggestionType[] = [
|
||||
// // literals
|
||||
// ... literals.map((resolved) => ["literal", text, resolved]),
|
||||
|
||||
// names
|
||||
... trie.suggest(env.names)(text)(Infinity)
|
||||
.map(([name, resolved]) => ["name", name, resolved]),
|
||||
]
|
||||
// return []; // <-- uncomment to disable suggestions (useful for debugging)
|
||||
return ls
|
||||
.map((suggestion: SuggestionType) =>
|
||||
[score(suggestion[2]), ...suggestion] as PrioritizedSuggestionType)
|
||||
.sort(([priorityA], [priorityB]) => priorityB - priorityA)
|
||||
}
|
||||
// // names
|
||||
// ... trie.suggest(env.names)(text)(Infinity)
|
||||
// .map(([name, resolved]) => ["name", name, resolved]),
|
||||
// ]
|
||||
// // return []; // <-- uncomment to disable suggestions (useful for debugging)
|
||||
// return ls
|
||||
// .map((suggestion: SuggestionType) =>
|
||||
// [score(suggestion[2]), ...suggestion] as PrioritizedSuggestionType)
|
||||
// .sort(([priorityA], [priorityB]) => priorityB - priorityA)
|
||||
// }
|
||||
|
||||
export function InputBlock({ state, setState, score, onCancel }: InputBlockProps) {
|
||||
export function InputBlock({ state, setState, /*score,*/ onCancel }: InputBlockProps) {
|
||||
const {text, focus} = state;
|
||||
const globalContext = useContext(GlobalContext);
|
||||
const env = useContext(EnvContext);
|
||||
|
|
@ -69,7 +70,8 @@ export function InputBlock({ state, setState, score, onCancel }: InputBlockProps
|
|||
const [i, setI] = useState(0); // selected suggestion idx
|
||||
|
||||
const singleSuggestion = trie.growPrefix(env.names)(text);
|
||||
const suggestions = useMemo(() => computeSuggestions(text, env, score), [text, score, env]);
|
||||
// const suggestions = useMemo(() => computeSuggestions(text, env, score), [text, score, env]);
|
||||
const suggestions = useMemo(() => [], []);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -94,21 +96,21 @@ export function InputBlock({ state, setState, score, onCancel }: InputBlockProps
|
|||
}
|
||||
|
||||
const onSelectSuggestion = () => {
|
||||
const [_priority, kind, name, dynamic] = suggestions[i];
|
||||
if (kind === "literal") {
|
||||
setState(state => ({
|
||||
...state,
|
||||
text: name,
|
||||
value: {kind, type: prettyT(getType(dynamic))},
|
||||
}));
|
||||
}
|
||||
else {
|
||||
setState(state => ({
|
||||
...state,
|
||||
text: name,
|
||||
value: {kind},
|
||||
}))
|
||||
}
|
||||
// const [_priority, kind, name, dynamic] = suggestions[i];
|
||||
// if (kind === "literal") {
|
||||
// setState(state => ({
|
||||
// ...state,
|
||||
// text: name,
|
||||
// value: {kind, type: prettyT(getType(dynamic))},
|
||||
// }));
|
||||
// }
|
||||
// else {
|
||||
// setState(state => ({
|
||||
// ...state,
|
||||
// text: name,
|
||||
// value: {kind},
|
||||
// }))
|
||||
// }
|
||||
};
|
||||
|
||||
const extraHandlers = {
|
||||
|
|
@ -134,7 +136,9 @@ export function InputBlock({ state, setState, score, onCancel }: InputBlockProps
|
|||
},
|
||||
};
|
||||
|
||||
return <Input
|
||||
const typeInfo = inferTypeInput(state, env);
|
||||
|
||||
return <><Input
|
||||
placeholder="<name or literal>"
|
||||
onCancel={onCancel}
|
||||
onEnter={onSelectSuggestion}
|
||||
|
|
@ -150,6 +154,8 @@ export function InputBlock({ state, setState, score, onCancel }: InputBlockProps
|
|||
i={i} setI={setI} />
|
||||
</span>
|
||||
</Input>
|
||||
::<Type type={typeInfo.type} />
|
||||
</>
|
||||
}
|
||||
|
||||
function Suggestions({ suggestions, onSelect, i, setI }) {
|
||||
|
|
@ -170,10 +176,10 @@ interface SuggestionProps {
|
|||
j: number;
|
||||
onSelect: any;
|
||||
highlighted: boolean;
|
||||
suggestion: PrioritizedSuggestionType;
|
||||
// suggestion: PrioritizedSuggestionType;
|
||||
}
|
||||
|
||||
function Suggestion({ setI, j, onSelect, highlighted, suggestion: [priority, kind, text, resolved] }: SuggestionProps) {
|
||||
function Suggestion({ setI, j, onSelect, highlighted, /*suggestion: [priority, kind, text, resolved]*/ }: SuggestionProps) {
|
||||
const onMouseEnter = j => () => {
|
||||
setI(j);
|
||||
};
|
||||
|
|
@ -186,7 +192,7 @@ function Suggestion({ setI, j, onSelect, highlighted, suggestion: [priority, kin
|
|||
className={(highlighted ? " selected" : "")}
|
||||
onMouseEnter={onMouseEnter(j)}
|
||||
onMouseDown={onMouseDown(j)}>
|
||||
({priority}) ({kind}) {text} :: <Type type={resolved.t} />
|
||||
{/* ({priority}) ({kind}) {text} :: <Type type={resolved.t} /> */}
|
||||
</div>
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import { useContext } from "react";
|
||||
|
||||
import { eqType, getSymbol, reduceUnification } from "dope2";
|
||||
// import { eqType, getSymbol, reduceUnification } from "dope2";
|
||||
|
||||
import { ExprBlock, type ExprBlockState, type State2Props } from "./ExprBlock";
|
||||
import { EnvContext } from "./EnvContext";
|
||||
import { evalExprBlock, evalLambdaBlock, makeInnerEnv, makeTypeVar } from "./eval";
|
||||
// import { evalExprBlock, evalLambdaBlock, makeInnerEnv, makeTypeVar } from "./eval";
|
||||
|
||||
import "./LambdaBlock.css";
|
||||
import { Type } from "./Type";
|
||||
import { Input } from "./Input";
|
||||
import { inferTypeLambda } from "./infer_type";
|
||||
|
||||
export interface LambdaBlockState {
|
||||
kind: "lambda";
|
||||
|
|
@ -36,9 +37,13 @@ export function LambdaBlock({state, setState, score}: LambdaBlockProps) {
|
|||
expr: callback(state.expr),
|
||||
}));
|
||||
|
||||
const [lambdaResolved, _, innerEnv] = evalLambdaBlock(state.paramName, state.expr, env);
|
||||
const {paramType, innerEnv} = inferTypeLambda(state, env);
|
||||
|
||||
const inferredParamType = lambdaResolved.t.params[0](lambdaResolved.t);
|
||||
// const [lambdaResolved, _, innerEnv] = evalLambdaBlock(state.paramName, state.expr, env);
|
||||
|
||||
// const inferredParamType = lambdaResolved.t.params[0](lambdaResolved.t);
|
||||
|
||||
// const innerEnv = env; // todo: change this
|
||||
|
||||
return <span className="lambdaBlock">
|
||||
<span className="keyword">λ</span>
|
||||
|
|
@ -55,7 +60,7 @@ export function LambdaBlock({state, setState, score}: LambdaBlockProps) {
|
|||
/>
|
||||
</span>
|
||||
<div className="typeSignature">
|
||||
:: <Type type={inferredParamType} />
|
||||
:: <Type type={paramType} />
|
||||
</div>
|
||||
|
||||
<span className="keyword">:</span>
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@ import { useContext } from "react";
|
|||
|
||||
import { ExprBlock, type ExprBlockState } from "./ExprBlock";
|
||||
import { EnvContext } from "./EnvContext";
|
||||
import { evalExprBlock, makeInnerEnv, scoreResolved, type ResolvedType } from "./eval";
|
||||
// import { evalExprBlock, makeInnerEnv, scoreResolved, type ResolvedType } from "./eval";
|
||||
import { type State2Props } from "./ExprBlock";
|
||||
import { GlobalContext } from "./GlobalContext";
|
||||
|
||||
import "./LetInBlock.css";
|
||||
import { Input } from "./Input";
|
||||
import { inferTypeLet } from "./infer_type";
|
||||
import { Type } from "./Type";
|
||||
|
||||
export interface LetInBlockState {
|
||||
kind: "let";
|
||||
|
|
@ -31,49 +33,54 @@ export function LetInBlock(props: LetInBlockProps) {
|
|||
</span>
|
||||
}
|
||||
|
||||
function DeclColumns({state: {name, value, inner, focus}, setState, score}) {
|
||||
function DeclColumns({state, setState, score}) {
|
||||
const env = useContext(EnvContext);
|
||||
const globalContext = useContext(GlobalContext);
|
||||
|
||||
const setInner = callback => setState(state => ({...state, inner: callback(state.inner)}));
|
||||
const setValue = callback => setState(state => ({...state, value: callback(state.value)}));
|
||||
|
||||
const valueSuggestionPriority = (suggestion: ResolvedType) => {
|
||||
const innerEnv = makeInnerEnv(env, name, suggestion);
|
||||
const [resolved] = evalExprBlock(inner, innerEnv);
|
||||
return scoreResolved(resolved, score);
|
||||
};
|
||||
// const valueSuggestionPriority = (suggestion: ResolvedType) => {
|
||||
// const innerEnv = makeInnerEnv(env, name, suggestion);
|
||||
// const [resolved] = evalExprBlock(inner, innerEnv);
|
||||
// return scoreResolved(resolved, score);
|
||||
// };
|
||||
|
||||
const [valueResolved] = evalExprBlock(value, env);
|
||||
const innerEnv = makeInnerEnv(env, name, valueResolved);
|
||||
// const [valueResolved] = evalExprBlock(value, env);
|
||||
// const innerEnv = makeInnerEnv(env, name, valueResolved);
|
||||
|
||||
const {paramType, innerEnv} = inferTypeLet(state, env);
|
||||
|
||||
// const innerEnv = env; // todo: change this
|
||||
|
||||
return <>
|
||||
<span className="keyword column">let </span>
|
||||
<span className="column rightAlign">
|
||||
<Input
|
||||
placeholder="<name>"
|
||||
text={name}
|
||||
text={state.name}
|
||||
suggestion=""
|
||||
onEnter={() => {}}
|
||||
onCancel={() => {}}
|
||||
onTextChange={name => setState(state => ({...state, name}))}
|
||||
extraHandlers={{}}
|
||||
/>
|
||||
:: <Type type={paramType} />
|
||||
</span>
|
||||
<span className="keyword column"> = </span>
|
||||
<span className="column">
|
||||
<ExprBlock
|
||||
state={value}
|
||||
state={state.value}
|
||||
setState={setValue}
|
||||
score={valueSuggestionPriority}
|
||||
score={() => 0}
|
||||
onCancel={() => setState(state => state.inner)} // keep inner
|
||||
/>
|
||||
</span>
|
||||
{inner.kind === "let" &&
|
||||
{state.inner.kind === "let" &&
|
||||
globalContext?.syntacticSugar &&
|
||||
<EnvContext value={innerEnv}>
|
||||
<DeclColumns
|
||||
state={inner}
|
||||
state={state.inner}
|
||||
setState={setInner}
|
||||
score={score}
|
||||
/>
|
||||
|
|
@ -86,8 +93,9 @@ function InnerMost({state, setState, score}) {
|
|||
const env = useContext(EnvContext);
|
||||
const globalContext = useContext(GlobalContext);
|
||||
const setInner = callback => setState(state => ({...state, inner: callback(state.inner)}));
|
||||
const [valueResolved] = evalExprBlock(state.value, env);
|
||||
const innerEnv = makeInnerEnv(env, state.name, valueResolved);
|
||||
// const [valueResolved] = evalExprBlock(state.value, env);
|
||||
// const innerEnv = makeInnerEnv(env, state.name, valueResolved);
|
||||
const innerEnv = env; // todo: change this
|
||||
const onCancel = () => setState(state => state.value);
|
||||
if (state.inner.kind === "let" && globalContext?.syntacticSugar) {
|
||||
return <EnvContext value={innerEnv}>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { initialEditorState } from "./configurations";
|
||||
import { removeFocus } from "./eval";
|
||||
// import { removeFocus } from "./eval";
|
||||
|
||||
const removeFocus = state => state;
|
||||
|
||||
export const actionShortcuts: [string, string[], string][] = [
|
||||
["call" , ['c'], "expr ⌴" ],
|
||||
|
|
|
|||
1148
src/eval.ts
1148
src/eval.ts
File diff suppressed because it is too large
Load diff
216
src/infer_type.ts
Normal file
216
src/infer_type.ts
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
import { Double, eqType, fnType, IncompatibleTypesError, Int, mergeSubstitutionsN, occurring, prettySS, recomputeTypeVars, substitute, trie, TYPE_VARS, UNBOUND_SYMBOLS, unify } from "dope2";
|
||||
|
||||
import type { CallBlockState } from "./CallBlock";
|
||||
import type { ExprBlockState } from "./ExprBlock";
|
||||
import type { InputBlockState } from "./InputBlock";
|
||||
import type { LambdaBlockState } from "./LambdaBlock";
|
||||
import type { LetInBlockState } from "./LetInBlock";
|
||||
|
||||
export interface Environment {
|
||||
names: any;
|
||||
typevars: Set<string>;
|
||||
}
|
||||
|
||||
export interface Type {
|
||||
symbol: string;
|
||||
params: ((t: Type) => Type)[];
|
||||
};
|
||||
|
||||
export type Substitutions = Map<string, Type>;
|
||||
|
||||
export interface TypeInfo {
|
||||
type: Type;
|
||||
subs: Substitutions;
|
||||
newEnv: Environment;
|
||||
err?: IncompatibleTypesError;
|
||||
}
|
||||
|
||||
export interface LambdaTypeInfo extends TypeInfo {
|
||||
paramType: Type;
|
||||
innerEnv: Environment;
|
||||
}
|
||||
|
||||
export function inferType(s: ExprBlockState, env: Environment): TypeInfo {
|
||||
if (s.kind === "input") {
|
||||
return inferTypeInput(s, env);
|
||||
}
|
||||
else if (s.kind === "call") {
|
||||
return inferTypeCall(s, env);
|
||||
}
|
||||
else if (s.kind === "let") {
|
||||
return inferTypeLet(s, env);
|
||||
}
|
||||
else { // (s.kind === "lambda")
|
||||
return inferTypeLambda(s, env);
|
||||
}
|
||||
}
|
||||
|
||||
export function inferTypeInput(s: InputBlockState, env: Environment): TypeInfo {
|
||||
if (s.value.kind === "literal") {
|
||||
const type = {
|
||||
Int: Int,
|
||||
Double: Double,
|
||||
}[s.value.type] as Type;
|
||||
return {
|
||||
type,
|
||||
subs: new Map(),
|
||||
newEnv: env,
|
||||
}
|
||||
}
|
||||
else if (s.value.kind === "name") {
|
||||
const found = trie.get(env.names)(s.text);
|
||||
if (found) {
|
||||
let type = found.t;
|
||||
let newEnv = env;
|
||||
if (found.kind !== "unknown") {
|
||||
[type, newEnv] = rewriteType(found.t, env);
|
||||
}
|
||||
return {
|
||||
type,
|
||||
subs: new Map(),
|
||||
newEnv,
|
||||
};
|
||||
}
|
||||
}
|
||||
// kind === "text", or name not found
|
||||
const [type, newEnv] = typeUnknown(env);
|
||||
return {
|
||||
type,
|
||||
subs: new Map(),
|
||||
newEnv,
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
export function inferTypeCall(s: CallBlockState, env: Environment): TypeInfo {
|
||||
const fnTypeInfo = inferType(s.fn, env);
|
||||
const inputTypeInfo = inferType(s.input, fnTypeInfo.newEnv);
|
||||
const [returnType, envWithReturn] = typeUnknown(inputTypeInfo.newEnv);
|
||||
const fakeFnType = fnType(_ => inputTypeInfo.type)(_ => returnType);
|
||||
try {
|
||||
const subs = unify(
|
||||
fnTypeInfo.type,
|
||||
fakeFnType);
|
||||
console.log("subs:", prettySS(subs));
|
||||
let type, newEnv;
|
||||
if (subs.has(returnType!.symbol)) {
|
||||
type = subs.get(returnType!.symbol);
|
||||
subs.delete(returnType!.symbol);
|
||||
newEnv = inputTypeInfo.newEnv
|
||||
}
|
||||
else {
|
||||
type = returnType;
|
||||
newEnv = envWithReturn;
|
||||
}
|
||||
const mergedSubs = mergeSubstitutionsN([
|
||||
fnTypeInfo.subs,
|
||||
inputTypeInfo.subs,
|
||||
subs,
|
||||
]);
|
||||
return {
|
||||
type,
|
||||
subs: mergedSubs,
|
||||
newEnv,
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof IncompatibleTypesError) {
|
||||
const [type, newEnv] = typeUnknown(env);
|
||||
return {
|
||||
type,
|
||||
subs: new Map(),
|
||||
newEnv,
|
||||
err: e,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function inferTypeLet(s: LetInBlockState, env: Environment): LambdaTypeInfo {
|
||||
const valTypeInfo = inferType(s.value, env);
|
||||
const innerEnv = {
|
||||
names: trie.insert(env.names)(s.name)({kind: "value", t: valTypeInfo.type}),
|
||||
typevars: env.typevars,
|
||||
};
|
||||
return {
|
||||
...inferType(s.inner, innerEnv),
|
||||
paramType: valTypeInfo.type,
|
||||
innerEnv,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function inferTypeLambda(s: LambdaBlockState, env: Environment): LambdaTypeInfo {
|
||||
let [paramType] = typeUnknown(env);
|
||||
const paramTypeVar = paramType.symbol;
|
||||
|
||||
let iterations = 1;
|
||||
while (true) {
|
||||
const innerEnv = {
|
||||
names: trie.insert(env.names)(s.paramName)({kind: "unknown", t: paramType}),
|
||||
typevars: env.typevars.union(occurring(paramType) as Set<string>),
|
||||
};
|
||||
const typeInfo = inferType(s.expr, innerEnv);
|
||||
const subsWithoutPType = new Map(typeInfo.subs);
|
||||
subsWithoutPType.delete(paramTypeVar);
|
||||
const inferredPType = substitute(paramType, typeInfo.subs, []);
|
||||
const inferredPType2 = rewriteInferredType(inferredPType, env);
|
||||
if (eqType(inferredPType2)(paramType)) {
|
||||
return {
|
||||
type: fnType(_ => paramType)(_ => typeInfo.type),
|
||||
subs: subsWithoutPType,
|
||||
newEnv: env, // no change
|
||||
paramType,
|
||||
innerEnv,
|
||||
};
|
||||
}
|
||||
if ((iterations++) == 4) {
|
||||
throw new Error("too many iterations!");
|
||||
}
|
||||
// console.log("-----------------", iterations);
|
||||
// console.log("paramType:", prettyT(paramType));
|
||||
// console.log("inferredPType:", prettyT(inferredPType));
|
||||
// console.log("inferredPType2:", prettyT(inferredPType2));
|
||||
// console.log("env:", [...env.typevars].map(getHumanReadableName));
|
||||
// console.log("innerEnv:", [...innerEnv.typevars].map(getHumanReadableName));
|
||||
// console.log("-----------------");
|
||||
paramType = inferredPType2;
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers
|
||||
const highestTypeVar2 = (typevars: Iterable<string>) => {
|
||||
let highest = -1;
|
||||
for (const typeVar of typevars) {
|
||||
highest = Math.max(highest, UNBOUND_SYMBOLS.indexOf(typeVar));
|
||||
}
|
||||
return highest;
|
||||
}
|
||||
function rewriteType(type: Type, env: Environment): [Type, Environment] {
|
||||
const [recomputed] = recomputeTypeVars([type], highestTypeVar2(env.typevars)+1);
|
||||
return [type, {
|
||||
names: env.names,
|
||||
typevars: env.typevars.union(occurring(recomputed)),
|
||||
}];
|
||||
}
|
||||
function typeUnknown(env: Environment): [Type, Environment] {
|
||||
const type = TYPE_VARS[highestTypeVar2(env.typevars)+1];
|
||||
const newEnv = {
|
||||
names: env.names,
|
||||
typevars: new Set([...env.typevars, type.symbol]),
|
||||
};
|
||||
return [type, newEnv];
|
||||
}
|
||||
function rewriteInferredType(type: Type, env: Environment) {
|
||||
const substitutions = new Map();
|
||||
let i = 0;
|
||||
for (const o of occurring(type)) {
|
||||
while (env.typevars.has(UNBOUND_SYMBOLS[i])) {
|
||||
i++;
|
||||
}
|
||||
if (!env.typevars.has(o)) {
|
||||
substitutions.set(o, TYPE_VARS[i]);
|
||||
}
|
||||
}
|
||||
return substitute(type, substitutions, []);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue