diff --git a/eslint.config.js b/eslint.config.js index 092408a..6530d94 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -10,7 +10,7 @@ export default tseslint.config( extends: [js.configs.recommended, ...tseslint.configs.recommended], files: ['**/*.{ts,tsx}'], languageOptions: { - ecmaVersion: 2020, + ecmaVersion: "latest", globals: globals.browser, }, plugins: { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a06fbf9..3f0f436 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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#664f31447fb313be3a01b80b220216b3374ef24f + version: git+https://deemz.org/git/joeri/dope2.git#cac0cb7f81c4359f8014caa508707ecf41e09092 react: specifier: ^19.1.0 version: 19.1.0 @@ -289,103 +289,103 @@ packages: '@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==} + '@rollup/rollup-android-arm-eabi@4.41.1': + resolution: {integrity: sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.41.0': - resolution: {integrity: sha512-yDvqx3lWlcugozax3DItKJI5j05B0d4Kvnjx+5mwiUpWramVvmAByYigMplaoAQ3pvdprGCTCE03eduqE/8mPQ==} + '@rollup/rollup-android-arm64@4.41.1': + resolution: {integrity: sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.41.0': - resolution: {integrity: sha512-2KOU574vD3gzcPSjxO0eyR5iWlnxxtmW1F5CkNOHmMlueKNCQkxR6+ekgWyVnz6zaZihpUNkGxjsYrkTJKhkaw==} + '@rollup/rollup-darwin-arm64@4.41.1': + resolution: {integrity: sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.41.0': - resolution: {integrity: sha512-gE5ACNSxHcEZyP2BA9TuTakfZvULEW4YAOtxl/A/YDbIir/wPKukde0BNPlnBiP88ecaN4BJI2TtAd+HKuZPQQ==} + '@rollup/rollup-darwin-x64@4.41.1': + resolution: {integrity: sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.41.0': - resolution: {integrity: sha512-GSxU6r5HnWij7FoSo7cZg3l5GPg4HFLkzsFFh0N/b16q5buW1NAWuCJ+HMtIdUEi6XF0qH+hN0TEd78laRp7Dg==} + '@rollup/rollup-freebsd-arm64@4.41.1': + resolution: {integrity: sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.41.0': - resolution: {integrity: sha512-KGiGKGDg8qLRyOWmk6IeiHJzsN/OYxO6nSbT0Vj4MwjS2XQy/5emsmtoqLAabqrohbgLWJ5GV3s/ljdrIr8Qjg==} + '@rollup/rollup-freebsd-x64@4.41.1': + resolution: {integrity: sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.41.0': - resolution: {integrity: sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA==} + '@rollup/rollup-linux-arm-gnueabihf@4.41.1': + resolution: {integrity: sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.41.0': - resolution: {integrity: sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg==} + '@rollup/rollup-linux-arm-musleabihf@4.41.1': + resolution: {integrity: sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.41.0': - resolution: {integrity: sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw==} + '@rollup/rollup-linux-arm64-gnu@4.41.1': + resolution: {integrity: sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.41.0': - resolution: {integrity: sha512-l+QK99je2zUKGd31Gh+45c4pGDAqZSuWQiuRFCdHYC2CSiO47qUWsCcenrI6p22hvHZrDje9QjwSMAFL3iwXwQ==} + '@rollup/rollup-linux-arm64-musl@4.41.1': + resolution: {integrity: sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.41.0': - resolution: {integrity: sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w==} + '@rollup/rollup-linux-loongarch64-gnu@4.41.1': + resolution: {integrity: sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.41.0': - resolution: {integrity: sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg==} + '@rollup/rollup-linux-powerpc64le-gnu@4.41.1': + resolution: {integrity: sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.41.0': - resolution: {integrity: sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A==} + '@rollup/rollup-linux-riscv64-gnu@4.41.1': + resolution: {integrity: sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.41.0': - resolution: {integrity: sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A==} + '@rollup/rollup-linux-riscv64-musl@4.41.1': + resolution: {integrity: sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.41.0': - resolution: {integrity: sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw==} + '@rollup/rollup-linux-s390x-gnu@4.41.1': + resolution: {integrity: sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.41.0': - resolution: {integrity: sha512-XMLeKjyH8NsEDCRptf6LO8lJk23o9wvB+dJwcXMaH6ZQbbkHu2dbGIUindbMtRN6ux1xKi16iXWu6q9mu7gDhQ==} + '@rollup/rollup-linux-x64-gnu@4.41.1': + resolution: {integrity: sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.41.0': - resolution: {integrity: sha512-m/P7LycHZTvSQeXhFmgmdqEiTqSV80zn6xHaQ1JSqwCtD1YGtwEK515Qmy9DcB2HK4dOUVypQxvhVSy06cJPEg==} + '@rollup/rollup-linux-x64-musl@4.41.1': + resolution: {integrity: sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.41.0': - resolution: {integrity: sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg==} + '@rollup/rollup-win32-arm64-msvc@4.41.1': + resolution: {integrity: sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.41.0': - resolution: {integrity: sha512-tmazCrAsKzdkXssEc65zIE1oC6xPHwfy9d5Ta25SRCDOZS+I6RypVVShWALNuU9bxIfGA0aqrmzlzoM5wO5SPQ==} + '@rollup/rollup-win32-ia32-msvc@4.41.1': + resolution: {integrity: sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.41.0': - resolution: {integrity: sha512-h1J+Yzjo/X+0EAvR2kIXJDuTuyT7drc+t2ALY0nIcGPbTatNOf0VWdhEA2Z4AAjv6X1NJV7SYo5oCTYRJhSlVA==} + '@rollup/rollup-win32-x64-msvc@4.41.1': + resolution: {integrity: sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==} cpu: [x64] os: [win32] @@ -600,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#664f31447fb313be3a01b80b220216b3374ef24f: - resolution: {commit: 664f31447fb313be3a01b80b220216b3374ef24f, repo: https://deemz.org/git/joeri/dope2.git, type: git} + dope2@git+https://deemz.org/git/joeri/dope2.git#cac0cb7f81c4359f8014caa508707ecf41e09092: + resolution: {commit: cac0cb7f81c4359f8014caa508707ecf41e09092, repo: https://deemz.org/git/joeri/dope2.git, type: git} version: 0.0.1 esbuild@0.25.4: @@ -891,8 +891,8 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.41.0: - resolution: {integrity: sha512-HqMFpUbWlf/tvcxBFNKnJyzc7Lk+XO3FGc3pbNBLqEbOz0gPLRgcrlS3UF4MfUrVlstOaP/q0kM6GVvi+LrLRg==} + rollup@4.41.1: + resolution: {integrity: sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -1165,64 +1165,64 @@ snapshots: '@rolldown/pluginutils@1.0.0-beta.9': {} - '@rollup/rollup-android-arm-eabi@4.41.0': + '@rollup/rollup-android-arm-eabi@4.41.1': optional: true - '@rollup/rollup-android-arm64@4.41.0': + '@rollup/rollup-android-arm64@4.41.1': optional: true - '@rollup/rollup-darwin-arm64@4.41.0': + '@rollup/rollup-darwin-arm64@4.41.1': optional: true - '@rollup/rollup-darwin-x64@4.41.0': + '@rollup/rollup-darwin-x64@4.41.1': optional: true - '@rollup/rollup-freebsd-arm64@4.41.0': + '@rollup/rollup-freebsd-arm64@4.41.1': optional: true - '@rollup/rollup-freebsd-x64@4.41.0': + '@rollup/rollup-freebsd-x64@4.41.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.41.0': + '@rollup/rollup-linux-arm-gnueabihf@4.41.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.41.0': + '@rollup/rollup-linux-arm-musleabihf@4.41.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.41.0': + '@rollup/rollup-linux-arm64-gnu@4.41.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.41.0': + '@rollup/rollup-linux-arm64-musl@4.41.1': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.41.0': + '@rollup/rollup-linux-loongarch64-gnu@4.41.1': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.41.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.41.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.41.0': + '@rollup/rollup-linux-riscv64-gnu@4.41.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.41.0': + '@rollup/rollup-linux-riscv64-musl@4.41.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.41.0': + '@rollup/rollup-linux-s390x-gnu@4.41.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.41.0': + '@rollup/rollup-linux-x64-gnu@4.41.1': optional: true - '@rollup/rollup-linux-x64-musl@4.41.0': + '@rollup/rollup-linux-x64-musl@4.41.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.41.0': + '@rollup/rollup-win32-arm64-msvc@4.41.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.41.0': + '@rollup/rollup-win32-ia32-msvc@4.41.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.41.0': + '@rollup/rollup-win32-x64-msvc@4.41.1': optional: true '@swc/core-darwin-arm64@1.11.29': @@ -1437,7 +1437,7 @@ snapshots: deep-is@0.1.4: {} - dope2@git+https://deemz.org/git/joeri/dope2.git#664f31447fb313be3a01b80b220216b3374ef24f: + dope2@git+https://deemz.org/git/joeri/dope2.git#cac0cb7f81c4359f8014caa508707ecf41e09092: dependencies: functional-red-black-tree: 1.0.1 @@ -1730,30 +1730,30 @@ snapshots: reusify@1.1.0: {} - rollup@4.41.0: + rollup@4.41.1: dependencies: '@types/estree': 1.0.7 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.41.0 - '@rollup/rollup-android-arm64': 4.41.0 - '@rollup/rollup-darwin-arm64': 4.41.0 - '@rollup/rollup-darwin-x64': 4.41.0 - '@rollup/rollup-freebsd-arm64': 4.41.0 - '@rollup/rollup-freebsd-x64': 4.41.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.41.0 - '@rollup/rollup-linux-arm-musleabihf': 4.41.0 - '@rollup/rollup-linux-arm64-gnu': 4.41.0 - '@rollup/rollup-linux-arm64-musl': 4.41.0 - '@rollup/rollup-linux-loongarch64-gnu': 4.41.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.41.0 - '@rollup/rollup-linux-riscv64-gnu': 4.41.0 - '@rollup/rollup-linux-riscv64-musl': 4.41.0 - '@rollup/rollup-linux-s390x-gnu': 4.41.0 - '@rollup/rollup-linux-x64-gnu': 4.41.0 - '@rollup/rollup-linux-x64-musl': 4.41.0 - '@rollup/rollup-win32-arm64-msvc': 4.41.0 - '@rollup/rollup-win32-ia32-msvc': 4.41.0 - '@rollup/rollup-win32-x64-msvc': 4.41.0 + '@rollup/rollup-android-arm-eabi': 4.41.1 + '@rollup/rollup-android-arm64': 4.41.1 + '@rollup/rollup-darwin-arm64': 4.41.1 + '@rollup/rollup-darwin-x64': 4.41.1 + '@rollup/rollup-freebsd-arm64': 4.41.1 + '@rollup/rollup-freebsd-x64': 4.41.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.41.1 + '@rollup/rollup-linux-arm-musleabihf': 4.41.1 + '@rollup/rollup-linux-arm64-gnu': 4.41.1 + '@rollup/rollup-linux-arm64-musl': 4.41.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.41.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.41.1 + '@rollup/rollup-linux-riscv64-gnu': 4.41.1 + '@rollup/rollup-linux-riscv64-musl': 4.41.1 + '@rollup/rollup-linux-s390x-gnu': 4.41.1 + '@rollup/rollup-linux-x64-gnu': 4.41.1 + '@rollup/rollup-linux-x64-musl': 4.41.1 + '@rollup/rollup-win32-arm64-msvc': 4.41.1 + '@rollup/rollup-win32-ia32-msvc': 4.41.1 + '@rollup/rollup-win32-x64-msvc': 4.41.1 fsevents: 2.3.3 run-parallel@1.2.0: @@ -1817,7 +1817,7 @@ snapshots: fdir: 6.4.4(picomatch@4.0.2) picomatch: 4.0.2 postcss: 8.5.3 - rollup: 4.41.0 + rollup: 4.41.1 tinyglobby: 0.2.13 optionalDependencies: fsevents: 2.3.3 diff --git a/src/component/app/App.tsx b/src/component/app/App.tsx index 88251e1..1d43adc 100644 --- a/src/component/app/App.tsx +++ b/src/component/app/App.tsx @@ -1,11 +1,14 @@ import { useEffect, useState } from 'react'; -import './App.css'; -import { ExprBlock, type ExprBlockState } from '../expr/ExprBlock'; +import { extendedEnv } from '../../context/EnvContext'; import { GlobalContext } from '../../context/GlobalContext'; -import { biggerExample, emptySet, factorial, higherOrder, higherOrder2Params, inc, initialEditorState, lambda2Params, nonEmptyEditorState, pushBool, tripleFunctionCallEditorState } from "./configurations"; +import { inferType, scoreTypeInfo } from '../../eval/infer_type'; +import { ExprBlock, type ExprBlockState } from '../expr/ExprBlock'; import { actionShortcuts } from './actions'; +import { biggerExample, emptySet, factorial, higherOrder, higherOrder2Params, inc, initialEditorState, lambda2Params, nonEmptyEditorState, pushBool, setOfListOfBool, tripleFunctionCallEditorState } from "./configurations"; // import { scoreResolved, type ResolvedType } from './eval'; +import './App.css'; + const examples: [string, ExprBlockState][] = [ ["empty editor" , initialEditorState ], @@ -19,6 +22,7 @@ const examples: [string, ExprBlockState][] = [ ["inc" , inc ], ["empty set" , emptySet ], ["factorial" , factorial ], + ["set of list of bool" , setOfListOfBool ], ]; type AppState = { @@ -124,7 +128,6 @@ export function App() { const onSelectExample = (e: React.SyntheticEvent) => { // @ts-ignore if (e.target.value >= 0) { - // @ts-ignore // @ts-ignore pushHistory(_ => examples[e.target.value][1]); } @@ -168,7 +171,10 @@ export function App() { state={appState.history.at(-1)!} setState={pushHistory} onCancel={() => {}} - score={() => 0} + score={(state: ExprBlockState) => { + const typeInfo = inferType(state, extendedEnv); + return scoreTypeInfo(typeInfo); + }} /> diff --git a/src/component/app/configurations.ts b/src/component/app/configurations.ts index 4f958d2..1878b6e 100644 --- a/src/component/app/configurations.ts +++ b/src/component/app/configurations.ts @@ -3,7 +3,7 @@ import type { ExprBlockState } from "../expr/ExprBlock"; export const initialEditorState: ExprBlockState = { kind: "input", text: "", - value: { kind: "text" }, + value: { kind: "gibberish" }, focus: true, }; @@ -75,7 +75,7 @@ export const tripleFunctionCallEditorState: ExprBlockState = { }, }; -export const biggerExample: ExprBlockState = {"kind":"let","focus":false,"inner":{"kind":"let","focus":false,"inner":{"kind":"let","focus":false,"inner":{"kind":"let","focus":false,"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":"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}}},"input":{"kind":"input","text":"id","value":{"kind":"name"},"focus":true}}},"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","focus":false,"paramName":"x","expr":{"kind":"input","text":"x","value":{"kind":"name"},"focus":false}}},"name":"inc","value":{"kind":"lambda","focus":false,"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}}}}; +export const biggerExample: ExprBlockState = {"kind":"let","focus":false,"inner":{"kind":"let","focus":false,"inner":{"kind":"let","focus":false,"inner":{"kind":"let","focus":false,"inner":{"kind":"input","text":"","value":{"kind":"gibberish"},"focus":false},"name":"myListInc","value":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.map","value":{"kind":"name"},"focus":false},"input":{"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}}},"input":{"kind":"input","text":"id","value":{"kind":"name"},"focus":true}}},"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","focus":false,"paramName":"x","expr":{"kind":"input","text":"x","value":{"kind":"name"},"focus":false}}},"name":"inc","value":{"kind":"lambda","focus":false,"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}}}}; export const lambda2Params: ExprBlockState = { "kind": "let", @@ -83,7 +83,7 @@ export const lambda2Params: ExprBlockState = { "kind": "input", "text": "", "value": { - "kind": "text" + "kind": "gibberish" }, "focus": false }, @@ -131,7 +131,7 @@ export const lambda2Params: ExprBlockState = { } }; -export const higherOrder: ExprBlockState = {"kind":"let","focus":false,"inner":{"kind":"input","text":"","value":{"kind":"text"},"focus":false},"name":"myBinaryApply","value":{"kind":"lambda","focus":false,"paramName":"x","expr":{"kind":"lambda","focus":false,"paramName":"fn","expr":{"kind":"call","fn":{"kind":"input","text":"fn","value":{"kind":"name"},"focus":false},"input":{"kind":"input","text":"x","value":{"kind":"name"},"focus":false}}}}}; +export const higherOrder: ExprBlockState = {"kind":"let","focus":false,"inner":{"kind":"input","text":"","value":{"kind":"gibberish"},"focus":false},"name":"myBinaryApply","value":{"kind":"lambda","focus":false,"paramName":"x","expr":{"kind":"lambda","focus":false,"paramName":"fn","expr":{"kind":"call","fn":{"kind":"input","text":"fn","value":{"kind":"name"},"focus":false},"input":{"kind":"input","text":"x","value":{"kind":"name"},"focus":false}}}}}; export const higherOrder2Params: ExprBlockState = {"kind":"let","focus":false,"inner":{"kind":"call","fn":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"myBinaryApply","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":"addInt","value":{"kind":"name"},"focus":true}},"name":"myBinaryApply","value":{"kind":"lambda","focus":false,"paramName":"x","expr":{"kind":"lambda","focus":false,"paramName":"y","expr":{"kind":"lambda","focus":false,"paramName":"fn","expr":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"fn","value":{"kind":"name"},"focus":false},"input":{"kind":"input","text":"x","value":{"kind":"name"},"focus":false}},"input":{"kind":"input","text":"y","value":{"kind":"name"},"focus":true}}}}}}; @@ -139,6 +139,8 @@ export const pushBool: ExprBlockState = {"kind":"call","fn":{"kind":"call","fn": export const inc: ExprBlockState = {"kind":"let","focus":false,"inner":{"kind":"input","text":"","value":{"kind":"name"},"focus":false},"name":"inc","value":{"kind":"lambda","focus":false,"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}}}}; -export const emptySet: ExprBlockState = {"kind":"call","fn":{"kind":"input","text":"set.emptySet","value":{"kind":"name"},"focus":false},"input":{"kind":"input","text":"","value":{"kind":"text"},"focus":true}}; +export const emptySet: ExprBlockState = {"kind":"call","fn":{"kind":"input","text":"set.emptySet","value":{"kind":"name"},"focus":false},"input":{"kind":"input","text":"","value":{"kind":"gibberish"},"focus":true}}; export const factorial: ExprBlockState = {"kind":"lambda","paramName":"factorial","focus":true,"expr":{"kind":"lambda","paramName":"n","focus":true,"expr":{"kind":"call","fn":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"leqZero","value":{"kind":"name"},"focus":false},"input":{"kind":"input","text":"n","value":{"kind":"name"},"focus":false}},"input":{"kind":"lambda","paramName":"_","focus":false,"expr":{"kind":"input","text":"1","value":{"kind":"literal","type":"Int"},"focus":false}}},"input":{"kind":"lambda","paramName":"_","focus":false,"expr":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"mulInt","value":{"kind":"name"},"focus":false},"input":{"kind":"input","text":"n","value":{"kind":"name"},"focus":true}},"input":{"kind":"call","fn":{"kind":"input","text":"factorial","value":{"kind":"name"},"focus":true},"input":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"addInt","value":{"kind":"name"},"focus":false},"input":{"kind":"input","text":"n","value":{"kind":"name"},"focus":false}},"input":{"kind":"input","text":"-1","value":{"kind":"literal","type":"Int"},"focus":false}}}}}}}}; + +export const setOfListOfBool: ExprBlockState = {"kind":"call","fn":{"kind":"input","text":"set.emptySet","value":{"kind":"name"},"focus":false},"input":{"kind":"call","fn":{"kind":"input","text":"compareLists","value":{"kind":"name"}},"input":{"kind":"input","text":"compareDoubles","value":{"kind":"name"}}}}; \ No newline at end of file diff --git a/src/component/expr/CallBlock.tsx b/src/component/expr/CallBlock.tsx index 42293d7..9e199fe 100644 --- a/src/component/expr/CallBlock.tsx +++ b/src/component/expr/CallBlock.tsx @@ -1,7 +1,6 @@ import { useContext } from "react"; import { EnvContext } from "../../context/EnvContext"; -// 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 "../../context/GlobalContext"; @@ -30,11 +29,7 @@ function nestedFnProperties({state, setState, score}: CallBlockProps, env: Envir setState(state => state.input); // we become our input }; const scoreFn = (fnSuggestion: ExprBlockState) => { - return score({ - kind: "call", - fn: fnSuggestion, - input: state.input, - }); + return score({ ...state, fn: fnSuggestion }); }; return {state: state.fn, setState: setFn, onCancel: onFnCancel, score: scoreFn}; } @@ -47,24 +42,11 @@ function nestedInputProperties({state, setState, score}: CallBlockProps, env: En setState(state => /*addFocusRightMost*/(state.fn)); // we become our function }; const scoreInput = (inputSuggestion: ExprBlockState) => { - return score({ - kind: "call", - fn: state.fn, - input: inputSuggestion, - }); + return score({ ...state, 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, env); -// const [inR, env3] = recomputeTypeVarsForEnv('', 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); @@ -80,9 +62,6 @@ export function CallBlock(props: CallBlockProps) { {/* Sequence of input parameters */} {/* { (resolved.kind === "error") && resolved.e.toString() @@ -113,20 +92,20 @@ function FunctionHeader(props) { } } -function InputParams({ depth, errorDepth, ...rest }) { +function InputParams({ ...rest }) { const env = useContext(EnvContext); const globalContext = useContext(GlobalContext); - const isOffending = depth === errorDepth; + const typeInfo = inferTypeCall(rest.state, env); + const inputEnv = typeInfo.fn.newEnv; + const isOffending = rest.state.err; return
{rest.state.fn.kind === "call" && globalContext?.syntacticSugar && } {/* Our own input param */} - + diff --git a/src/component/expr/ExprBlock.tsx b/src/component/expr/ExprBlock.tsx index f995d0b..d5beb51 100644 --- a/src/component/expr/ExprBlock.tsx +++ b/src/component/expr/ExprBlock.tsx @@ -1,7 +1,5 @@ import { useContext } from "react"; -import { getType } from "dope2"; - import { CallBlock, type CallBlockProps, type CallBlockState } from "./CallBlock"; import { EnvContext } from "../../context/EnvContext"; import { GlobalContext } from "../../context/GlobalContext"; @@ -13,6 +11,7 @@ import { LetInBlock, type LetInBlockProps, type LetInBlockState } from "./LetInB import "./ExprBlock.css"; import { Input } from "../other/Input"; import { getActions } from "../app/actions"; +import { inferType, type Type } from "../../eval/infer_type"; export type ExprBlockState = InputBlockState @@ -49,16 +48,14 @@ export function ExprBlock(props: ExprBlockProps) { const extraHandlers = Object.fromEntries(Object.entries(actions).map(([shortcut, action]) => [shortcut, (e) => { e.preventDefault(); action(); }])) - // return + const typeInfo = inferType(props.state, env); - return + return {renderBlock[props.state.kind]()} - {/* @ts-ignore */} - {/*
*/} - {/*  ::  */} - {/* @ts-ignore */} - {/* {resolved.__debug &&
{resolved.__debug}
} */} - {/*
*/} + {typeInfo.err && +
+ {typeInfo.err.message.split('\n')[1]} +
} { 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 attemptLiterals = [ + ["Double", text => (text !== '') && !Number.isNaN(Number(text))], + ["Int" , text => /^-?[0-9]+$/.test(text)] +] as [string, (text: string) => boolean][]; -// // 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) -// } +const computeSuggestions = ( + text: string, + env: Environment, + score: InputBlockProps['score'], +): PrioritizedSuggestionType[] => { + const ls = [ + ...attemptLiterals + .filter(([_, test]) => test(text)) + .map(([typename]) => ({ + kind: "input", + text, + value: { + kind: "literal", + type: typename, + }, + })), + ...trie.suggest(env.names)(text)(Infinity) + .map(([name]) => ({ + kind: "input", + text: name, + value: { + kind: "name", + }, + })), + ]; + // return []; // <-- uncomment to disable suggestions (useful for debugging) + return ls.map((state) => [score(state), inferType(state, env).type, state] as PrioritizedSuggestionType) + .sort(([a],[b]) => b-a); +} -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); @@ -70,8 +81,7 @@ export function InputBlock({ state, setState, /*score,*/ onCancel }: InputBlockP 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(() => [], []); + const suggestions = useMemo(() => computeSuggestions(text, env, score), [text, score, env]); useEffect(() => { @@ -96,21 +106,8 @@ export function InputBlock({ state, setState, /*score,*/ onCancel }: InputBlockP } 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, _type, inputState] = suggestions[i]; + setState(_ => inputState); }; const extraHandlers = { @@ -154,7 +151,7 @@ export function InputBlock({ state, setState, /*score,*/ onCancel }: InputBlockP i={i} setI={setI} />
- :: + :: } @@ -176,10 +173,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, type, {text, value: {kind} }] }: SuggestionProps) { const onMouseEnter = j => () => { setI(j); }; @@ -192,7 +189,7 @@ function Suggestion({ setI, j, onSelect, highlighted, /*suggestion: [priority, k className={(highlighted ? " selected" : "")} onMouseEnter={onMouseEnter(j)} onMouseDown={onMouseDown(j)}> - {/* ({priority}) ({kind}) {text} :: */} + ({priority}) ({kind}) {text} ::
} diff --git a/src/component/expr/LambdaBlock.tsx b/src/component/expr/LambdaBlock.tsx index 8efa9f3..77b59a4 100644 --- a/src/component/expr/LambdaBlock.tsx +++ b/src/component/expr/LambdaBlock.tsx @@ -71,10 +71,7 @@ export function LambdaBlock({state, setState, score}: LambdaBlockProps) { state={state.expr} setState={setExpr} onCancel={() => setState(state => state.expr)} - score={(s) => { - // console.log('suggestionPriority of lambdaInner... just passing through'); - return score(s); - }} + score={suggestion => score({...state, expr: suggestion})} /> diff --git a/src/component/expr/LetInBlock.tsx b/src/component/expr/LetInBlock.tsx index 7bd7969..1a4ce41 100644 --- a/src/component/expr/LetInBlock.tsx +++ b/src/component/expr/LetInBlock.tsx @@ -2,7 +2,6 @@ import { useContext } from "react"; import { ExprBlock, type ExprBlockState } from "./ExprBlock"; import { EnvContext } from "../../context/EnvContext"; -// import { evalExprBlock, makeInnerEnv, scoreResolved, type ResolvedType } from "./eval"; import { type State2Props } from "./ExprBlock"; import { GlobalContext } from "../../context/GlobalContext"; @@ -40,16 +39,7 @@ function DeclColumns({state, setState, score}) { 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 [valueResolved] = evalExprBlock(value, env); - // const innerEnv = makeInnerEnv(env, name, valueResolved); - - const {paramType, innerEnv} = inferTypeLet(state, env); + const {value: valueTypeInfo, innerEnv} = inferTypeLet(state, env); return <> let  @@ -63,14 +53,14 @@ function DeclColumns({state, setState, score}) { onTextChange={name => setState(state => ({...state, name}))} extraHandlers={{}} /> - :: + ::  =  0} + score={suggestion => score({ ...state, value: suggestion })} onCancel={() => setState(state => state.inner)} // keep inner /> @@ -80,7 +70,7 @@ function DeclColumns({state, setState, score}) { score({ ...state, inner: suggestion })} /> } @@ -93,14 +83,14 @@ function InnerMost({state, setState, score}) { const setInner = callback => setState(state => ({...state, inner: callback(state.inner)})); // const [valueResolved] = evalExprBlock(state.value, env); // const innerEnv = makeInnerEnv(env, state.name, valueResolved); - const {paramType, innerEnv} = inferTypeLet(state, env); + const {innerEnv} = inferTypeLet(state, env); const onCancel = () => setState(state => state.value); if (state.inner.kind === "let" && globalContext?.syntacticSugar) { return score({ ...state, inner: suggestion })} /> ; } @@ -109,7 +99,7 @@ function InnerMost({state, setState, score}) { score({ ...state, inner: suggestion })} onCancel={onCancel} // keep value /> diff --git a/src/context/EnvContext.ts b/src/context/EnvContext.ts index e540318..fd3dc15 100644 --- a/src/context/EnvContext.ts +++ b/src/context/EnvContext.ts @@ -1,7 +1,7 @@ import { createContext } from "react"; import { getDefaultTypeParser, module2Env, ModuleStd } from "dope2"; // import type { Dynamic, Environment } from "./eval"; -import type { Environment } from "./infer_type"; +import type { Environment } from "../eval/infer_type"; export const functionWith3Params = i => j => k => i+j+k; export const functionWith4Params = i => j => k => l => i+j+k+l; diff --git a/src/eval/eval.ts b/src/eval/eval.ts index 35457b1..972f2af 100644 --- a/src/eval/eval.ts +++ b/src/eval/eval.ts @@ -460,53 +460,6 @@ // } // } -// function parseLiteral(text: string, type: string, env: Environment): [ResolvedType,Environment] { -// // dirty -// if (type === "Int") { -// return parseAsInt(text, env); -// } -// if (type === "Double") { -// return parseAsDouble(text, env); -// } -// return makeError(env, new Error("Failed to parse")); -// } - -// function parseAsDouble(text: string, env: Environment): [ResolvedType,Environment] { -// if (text !== '') { -// const num = Number(text); -// if (!Number.isNaN(num)) { -// return [{ -// kind: "value", -// i: num, -// t: Double, -// unification: new Map(), -// }, env]; -// } -// } -// return makeError(env, new Error("Failed to parse as Double")); -// } -// function parseAsInt(text: string, env: Environment): [ResolvedType,Environment] { -// if (text !== '') { -// try { -// return [{ -// kind: "value", -// i: BigInt(text), -// t: Int, -// unification: new Map(), -// }, env]; // may throw -// } -// catch {} -// } -// return makeError(env, new Error("Failed to parse as Int")); -// } - -// const literalParsers = [parseAsDouble, parseAsInt]; - -// export function attemptParseLiteral(text: string, env: Environment): Dynamic[] { -// return literalParsers.map(parseFn => parseFn(text, env)) -// .map(([resolved]) => resolved) -// .filter((resolved) => (resolved.kind !== "unknown" && resolved.kind !== "error")) as unknown as Dynamic[]; -// } // export function scoreResolved(resolved: ResolvedType, outPriority: (s:ResolvedType) => number) { // const bias = outPriority(resolved); diff --git a/src/eval/infer_type.ts b/src/eval/infer_type.ts index 5ae322f..218e2f4 100644 --- a/src/eval/infer_type.ts +++ b/src/eval/infer_type.ts @@ -1,4 +1,4 @@ -import { Double, eqType, fnType, IncompatibleTypesError, Int, mergeSubstitutionsN, occurring, prettySS, recomputeTypeVars, substitute, trie, TYPE_VARS, UNBOUND_SYMBOLS, unify } from "dope2"; +import { Double, eqType, fnType, IncompatibleTypesError, Int, mergeSubstitutionsN, occurring, prettyS, prettySS, prettyT, recomputeTypeVars, substitute, SubstitutionCycle, trie, TYPE_VARS, UNBOUND_SYMBOLS, unify } from "dope2"; import type { CallBlockState } from "../component/expr/CallBlock"; import type { ExprBlockState } from "../component/expr/ExprBlock"; @@ -18,20 +18,35 @@ export interface Type { export type Substitutions = Map; -export interface TypeInfo { +interface TypeInfoCommon { type: Type; subs: Substitutions; newEnv: Environment; err?: IncompatibleTypesError; } -export interface TypeInfoLambda extends TypeInfo { +export interface TypeInfoInput extends TypeInfoCommon { + kind: "input"; +} +export interface TypeInfoCall extends TypeInfoCommon { + kind: "call"; + fn: TypeInfo; + input: TypeInfo; +} +export interface TypeInfoLet extends TypeInfoCommon { + kind: "let"; + innerEnv: Environment; + value: TypeInfo; + inner: TypeInfo; +} +export interface TypeInfoLambda extends TypeInfoCommon { + kind: "lambda"; paramType: Type; + inner: TypeInfo; innerEnv: Environment; } -export interface TypeInfoCall extends TypeInfo { - inputEnv: Environment; -} + +export type TypeInfo = TypeInfoInput | TypeInfoCall | TypeInfoLet | TypeInfoLambda; export function inferType(s: ExprBlockState, env: Environment): TypeInfo { if (s.kind === "input") { @@ -48,13 +63,14 @@ export function inferType(s: ExprBlockState, env: Environment): TypeInfo { } } -export function inferTypeInput(s: InputBlockState, env: Environment): TypeInfo { +export function inferTypeInput(s: InputBlockState, env: Environment): TypeInfoInput { if (s.value.kind === "literal") { const type = { Int: Int, Double: Double, }[s.value.type] as Type; return { + kind: "input", type, subs: new Map(), newEnv: env, @@ -67,18 +83,21 @@ export function inferTypeInput(s: InputBlockState, env: Environment): TypeInfo { ? [found.t, env] : rewriteType(found.t, env); return { + kind: "input", type, subs: new Map(), newEnv, }; } } - // kind === "text", or name not found + // kind === "gibberish", or name not found const [type, newEnv] = typeUnknown(env); return { + kind: "input", type, subs: new Map(), newEnv, + err: new Error("Gibberish"), } } @@ -104,41 +123,59 @@ export function inferTypeCall(s: CallBlockState, env: Environment): TypeInfoCall type = returnType; newEnv = envWithReturn; } - const mergedSubs = mergeSubstitutionsN([ - fnTypeInfo.subs, - inputTypeInfo.subs, - subs, - ]); + let mergedSubs; + try { + mergedSubs = mergeSubstitutionsN([ + fnTypeInfo.subs, + inputTypeInfo.subs, + subs, + ]); + } catch (e) { + if (e instanceof SubstitutionCycle) { + // wrap error + throw new IncompatibleTypesError(fnTypeInfo.type, fakeFnType, e); + } + throw e; + } return { + kind: "call", type, subs: mergedSubs, newEnv, - inputEnv, + fn: fnTypeInfo, + input: inputTypeInfo, }; } catch (e) { if (e instanceof IncompatibleTypesError) { const [type, newEnv] = typeUnknown(env); return { + kind: "call", type, subs: new Map(), newEnv, err: e, - inputEnv, + fn: fnTypeInfo, + input: inputTypeInfo, } } } } -export function inferTypeLet(s: LetInBlockState, env: Environment): TypeInfoLambda { +export function inferTypeLet(s: LetInBlockState, env: Environment): TypeInfoLet { const valTypeInfo = inferType(s.value, env); const innerEnv = { names: trie.insert(env.names)(s.name)({kind: "value", t: valTypeInfo.type}), typevars: env.typevars, }; + const innerTypeInfo = inferType(s.inner, innerEnv); return { - ...inferType(s.inner, innerEnv), - paramType: valTypeInfo.type, + kind: "let", + type: innerTypeInfo.type, + subs: innerTypeInfo.subs, + newEnv: env, + value: valTypeInfo, + inner: innerTypeInfo, innerEnv, }; } @@ -154,21 +191,23 @@ export function inferTypeLambda(s: LambdaBlockState, env: Environment): TypeInfo names: trie.insert(env.names)(s.paramName)({kind: "unknown", t: paramType}), typevars: env.typevars.union(occurring(paramType) as Set), }; - const typeInfo = inferType(s.expr, innerEnv); - const subsWithoutPType = new Map(typeInfo.subs); + const innerTypeInfo = inferType(s.expr, innerEnv); + const subsWithoutPType = new Map(innerTypeInfo.subs); subsWithoutPType.delete(paramTypeVar); - const inferredPType = substitute(paramType, typeInfo.subs, []); + const inferredPType = substitute(paramType, innerTypeInfo.subs, []); const inferredPType2 = rewriteInferredType(inferredPType, env); if (eqType(inferredPType2)(paramType)) { return { - type: fnType(_ => paramType)(_ => typeInfo.type), + kind: "lambda", + type: fnType(_ => paramType)(_ => innerTypeInfo.type), subs: subsWithoutPType, newEnv: env, // no change paramType, + inner: innerTypeInfo, innerEnv, }; } - if ((iterations++) == 4) { + if ((iterations++) == 10) { throw new Error("too many iterations!"); } // console.log("-----------------", iterations); @@ -192,9 +231,10 @@ const highestTypeVar2 = (typevars: Iterable) => { } function rewriteType(type: Type, env: Environment): [Type, Environment] { const [recomputed] = recomputeTypeVars([type], highestTypeVar2(env.typevars)+1); - return [type, { + const newTypeVars = occurring(recomputed); + return [recomputed, { names: env.names, - typevars: env.typevars.union(occurring(recomputed)), + typevars: env.typevars.union(newTypeVars), }]; } function typeUnknown(env: Environment): [Type, Environment] { @@ -218,3 +258,19 @@ function rewriteInferredType(type: Type, env: Environment) { } return substitute(type, substitutions, []); } +export function scoreTypeInfo(typeInfo: TypeInfo): number { + const bias = typeInfo.err ? -1 : 0; + if (typeInfo.kind === "input") { + return bias; + } + else if (typeInfo.kind === "call") { + return bias + scoreTypeInfo(typeInfo.fn) + scoreTypeInfo(typeInfo.input); + } + else if (typeInfo.kind === "let") { + return bias + scoreTypeInfo(typeInfo.value) + scoreTypeInfo(typeInfo.inner); + } + else if (typeInfo.kind === "lambda") { + return bias + scoreTypeInfo(typeInfo.inner); + } + return 0; // shut up typescript +}