From bc2d65e668a93f96545c1abddc38875a9e4d7a74 Mon Sep 17 00:00:00 2001 From: Peter Date: Mon, 10 Oct 2022 18:53:05 +0200 Subject: [PATCH 1/4] start universal entrypoint stuff --- editor/package-lock.json | 22 + editor/package.json | 1 + editor/public/saved.json | 952 +++++++--------------- editor/rollup.config.js | 143 ++-- editor/src/main.js | 1 + editor/src/other.js | 19 + eyg/src/eyg/analysis.gleam | 4 + eyg/src/eyg/ast/encode.gleam | 5 +- eyg/src/eyg/entry.gleam | 46 ++ eyg/src/eyg/interpreter/effectful.gleam | 16 + eyg/src/eyg/interpreter/interpreter.gleam | 4 +- 11 files changed, 510 insertions(+), 703 deletions(-) create mode 100644 editor/src/other.js create mode 100644 eyg/src/eyg/entry.gleam diff --git a/editor/package-lock.json b/editor/package-lock.json index a18bf4384..15d60113b 100644 --- a/editor/package-lock.json +++ b/editor/package-lock.json @@ -12,6 +12,7 @@ }, "devDependencies": { "@rollup/plugin-commonjs": "^17.0.0", + "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^11.0.0", "rollup": "^2.3.4", "rollup-plugin-css-only": "^3.1.0", @@ -82,6 +83,18 @@ "rollup": "^2.30.0" } }, + "node_modules/@rollup/plugin-json": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-4.1.0.tgz", + "integrity": "sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.0.8" + }, + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0" + } + }, "node_modules/@rollup/plugin-node-resolve": { "version": "11.2.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", @@ -1087,6 +1100,15 @@ "resolve": "^1.17.0" } }, + "@rollup/plugin-json": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-4.1.0.tgz", + "integrity": "sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.0.8" + } + }, "@rollup/plugin-node-resolve": { "version": "11.2.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", diff --git a/editor/package.json b/editor/package.json index 07400ffc7..2ba40ccbd 100644 --- a/editor/package.json +++ b/editor/package.json @@ -9,6 +9,7 @@ }, "devDependencies": { "@rollup/plugin-commonjs": "^17.0.0", + "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^11.0.0", "rollup": "^2.3.4", "rollup-plugin-css-only": "^3.1.0", diff --git a/editor/public/saved.json b/editor/public/saved.json index 901588894..ffa20a566 100644 --- a/editor/public/saved.json +++ b/editor/public/saved.json @@ -2698,17 +2698,12 @@ "value": { "node": "Function", "pattern": { - "node": "Tuple", - "elements": [ - { - "node": "Bind", - "label": "" - } - ] + "node": "Variable", + "label": "state" }, "body": { - "node": "Binary", - "value": "hello" + "node": "Variable", + "label": "state" } }, "then": { @@ -2921,13 +2916,8 @@ "label": "new" }, "value": { - "node": "Tuple", - "elements": [ - { - "node": "Variable", - "label": "old" - } - ] + "node": "Variable", + "label": "old" }, "then": { "node": "Call", @@ -2943,572 +2933,157 @@ } }, "then": { - "node": "Record", - "fields": [ - { - "node": "Field", - "key": "init", - "value": { - "node": "Call", - "function": { - "node": "Variable", - "label": "run" - }, - "with": { - "node": "Tuple", - "elements": [ - { - "node": "Binary", - "value": "" - } - ] - } - } - }, - { - "node": "Field", - "key": "release", - "value": { - "node": "Variable", - "label": "release" - } - } - ] - } - } - } - }, - "then": { - "node": "Let", - "pattern": { - "node": "Variable", - "label": "reload" - }, - "value": { - "node": "Function", - "pattern": { - "node": "Tuple", - "elements": [] - }, - "body": { - "node": "Let", - "pattern": { - "node": "Variable", - "label": "release" - }, - "value": { - "node": "Function", + "node": "Let", "pattern": { "node": "Variable", - "label": "old" + "label": "init" }, - "body": { - "node": "Let", - "pattern": { - "node": "Tuple", - "elements": [] - }, - "value": { - "node": "Variable", - "label": "old" - }, - "then": { - "node": "Let", - "pattern": { - "node": "Variable", - "label": "new" - }, - "value": { - "node": "Tuple", - "elements": [ - { - "node": "Variable", - "label": "old" - } - ] - }, - "then": { - "node": "Let", - "pattern": { - "node": "Variable", - "label": "run" - }, + "value": { + "node": "Record", + "fields": [ + { + "node": "Field", + "key": "page", "value": { - "node": "Function", - "pattern": { - "node": "Variable", - "label": "state" - }, - "body": { - "node": "Function", - "pattern": { - "node": "Variable", - "label": "interrupt" - }, - "body": { - "node": "Case", - "value": { - "node": "Variable", - "label": "interrupt" - }, - "branches": [ - { - "node": "Branch", - "name": "Click", - "pattern": { - "node": "Variable", - "label": "target" - }, - "then": { - "node": "Let", - "pattern": { - "node": "Variable", - "label": "" - }, - "value": { - "node": "Call", - "function": { - "node": "Variable", - "label": "equal" - }, - "with": { - "node": "Tuple", - "elements": [ - { - "node": "Binary", - "value": "" - }, - { - "node": "Variable", - "label": "target" - } - ] - } - }, - "then": { - "node": "Record", - "fields": [ - { - "node": "Field", - "key": "page", - "value": { - "node": "Binary", - "value": "render" - } - }, - { - "node": "Field", - "key": "interrupt", - "value": { - "node": "Call", - "function": { - "node": "Variable", - "label": "run" - }, - "with": { - "node": "Variable", - "label": "state" - } - } - } - ] - } - } - }, - { - "node": "Branch", - "name": "Code", - "pattern": { - "node": "Variable", - "label": "source" - }, - "then": { - "node": "Case", - "value": { - "node": "Call", - "function": { - "node": "Provider", - "config": "", - "generator": "Loader" - }, - "with": { - "node": "Variable", - "label": "source" - } - }, - "branches": [ - { - "node": "Branch", - "name": "Error", - "pattern": { - "node": "Variable", - "label": "" - }, - "then": { - "node": "Hole" - } - }, - { - "node": "Branch", - "name": "OK", - "pattern": { - "node": "Variable", - "label": "release" - }, - "then": { - "node": "Call", - "function": { - "node": "Variable", - "label": "release" - }, - "with": { - "node": "Variable", - "label": "state" - } - } - } - ] - } - } - ] - } - } - }, - "then": { "node": "Call", "function": { "node": "Variable", - "label": "run" + "label": "render" }, "with": { - "node": "Variable", - "label": "new" + "node": "Binary", + "value": "initial state" } } - } - } - } - }, - "then": { - "node": "Let", - "pattern": { - "node": "Variable", - "label": "run" - }, - "value": { - "node": "Function", - "pattern": { - "node": "Variable", - "label": "state" - }, - "body": { - "node": "Function", - "pattern": { - "node": "Variable", - "label": "interupt" }, - "body": { - "node": "Case", + { + "node": "Field", + "key": "interrupt", "value": { - "node": "Variable", - "label": "interupt" - }, - "branches": [ - { - "node": "Branch", - "name": "Click", - "pattern": { - "node": "Variable", - "label": "target" - }, - "then": { - "node": "Let", - "pattern": { - "node": "Variable", - "label": "" - }, - "value": { - "node": "Call", - "function": { - "node": "Variable", - "label": "equal" - }, - "with": { - "node": "Tuple", - "elements": [ - { - "node": "Binary", - "value": "" - }, - { - "node": "Variable", - "label": "target" - } - ] - } - }, - "then": { - "node": "Record", - "fields": [ - { - "node": "Field", - "key": "page", - "value": { - "node": "Binary", - "value": "render" - } - }, - { - "node": "Field", - "key": "interrupt", - "value": { - "node": "Call", - "function": { - "node": "Variable", - "label": "run" - }, - "with": { - "node": "Variable", - "label": "state" - } - } - } - ] - } - } + "node": "Call", + "function": { + "node": "Variable", + "label": "run" }, - { - "node": "Branch", - "name": "Code", - "pattern": { - "node": "Variable", - "label": "source" - }, - "then": { - "node": "Case", - "value": { - "node": "Call", - "function": { - "node": "Provider", - "config": "", - "generator": "Loader" - }, - "with": { - "node": "Variable", - "label": "source" - } - }, - "branches": [ - { - "node": "Branch", - "name": "Error", - "pattern": { - "node": "Variable", - "label": "" - }, - "then": { - "node": "Record", - "fields": [ - { - "node": "Field", - "key": "page", - "value": { - "node": "Binary", - "value": "render" - } - }, - { - "node": "Field", - "key": "interrupt", - "value": { - "node": "Call", - "function": { - "node": "Variable", - "label": "run" - }, - "with": { - "node": "Variable", - "label": "state" - } - } - } - ] - } - }, - { - "node": "Branch", - "name": "OK", - "pattern": { - "node": "Variable", - "label": "release" - }, - "then": { - "node": "Call", - "function": { - "node": "Variable", - "label": "release" - }, - "with": { - "node": "Variable", - "label": "state" - } - } - } - ] - } + "with": { + "node": "Binary", + "value": "initial state" } - ] + } } - } + ] }, "then": { - "node": "Let", - "pattern": { - "node": "Variable", - "label": "handle" - }, - "value": { - "node": "Function", - "pattern": { - "node": "Tuple", - "elements": [ - { - "node": "Bind", - "label": "interrupt" - }, - { - "node": "Bind", - "label": "state" - } - ] - }, - "body": { - "node": "Let", - "pattern": { - "node": "Variable", - "label": "" - }, + "node": "Record", + "fields": [ + { + "node": "Field", + "key": "init", "value": { - "node": "Case", - "value": { - "node": "Hole" - }, - "branches": [ - { - "node": "Branch", - "name": "Update", - "pattern": { - "node": "Variable", - "label": "source" - }, - "then": { - "node": "Hole" - } - } - ] - }, - "then": { - "node": "Let", - "pattern": { - "node": "Variable", - "label": "" - }, - "value": { - "node": "Tuple", - "elements": [] - }, - "then": { - "node": "Variable", - "label": "state" - } + "node": "Variable", + "label": "init" } - } - }, - "then": { - "node": "Let", - "pattern": { - "node": "Variable", - "label": "update" }, - "value": { - "node": "Function", - "pattern": { + { + "node": "Field", + "key": "release", + "value": { "node": "Variable", - "label": "state" - }, - "body": { - "node": "Let", - "pattern": { - "node": "Variable", - "label": "page" - }, - "value": { - "node": "Binary", - "value": "render" - }, - "then": { - "node": "Record", - "fields": [ - { - "node": "Field", - "key": "interrupt", - "value": { - "node": "Function", - "pattern": { - "node": "Variable", - "label": "i" - }, - "body": { - "node": "Call", - "function": { - "node": "Variable", - "label": "update" - }, - "with": { - "node": "Call", - "function": { - "node": "Variable", - "label": "handle" - }, - "with": { - "node": "Tuple", - "elements": [ - { - "node": "Variable", - "label": "i" - }, - { - "node": "Variable", - "label": "state" - } - ] - } - } - } - } - } - ] - } + "label": "release" } + } + ] + } + } + } + } + }, + "then": { + "node": "Let", + "pattern": { + "node": "Variable", + "label": "web" + }, + "value": { + "node": "Function", + "pattern": { + "node": "Variable", + "label": "req" + }, + "body": { + "node": "Case", + "value": { + "node": "Call", + "function": { + "node": "Variable", + "label": "equal" + }, + "with": { + "node": "Tuple", + "elements": [ + { + "node": "Binary", + "value": "/code" }, - "then": { - "node": "Call", - "function": { + { + "node": "Access", + "value": { "node": "Variable", - "label": "update" + "label": "req" }, - "with": { - "node": "Binary", - "value": "0" - } + "key": "path" } + ] + } + }, + "branches": [ + { + "node": "Branch", + "name": "False", + "pattern": { + "node": "Tuple", + "elements": [] + }, + "then": { + "node": "Binary", + "value": "" + } + }, + { + "node": "Branch", + "name": "True", + "pattern": { + "node": "Tuple", + "elements": [] + }, + "then": { + "node": "Binary", + "value": "TODO encode" } } - } + ] } }, "then": { - "node": "Hole" + "node": "Record", + "fields": [ + { + "node": "Field", + "key": "counter", + "value": { + "node": "Variable", + "label": "counter" + } + }, + { + "node": "Field", + "key": "web", + "value": { + "node": "Variable", + "label": "web" + } + } + ] } } } @@ -4459,11 +4034,15 @@ "elements": [ { "node": "Binary", - "value": "localhost:5003" + "value": "localhost:5004" }, { - "node": "Variable", - "label": "counter" + "node": "Access", + "value": { + "node": "Variable", + "label": "reload" + }, + "key": "web" } ] }, @@ -4491,7 +4070,7 @@ "elements": [ { "node": "Binary", - "value": "counter.web.petersaxton.uk" + "value": "localhost:5003" }, { "node": "Variable", @@ -4523,11 +4102,11 @@ "elements": [ { "node": "Binary", - "value": "localhost:5002" + "value": "counter.web.petersaxton.uk" }, { "node": "Variable", - "label": "cluster" + "label": "counter" } ] }, @@ -4555,7 +4134,7 @@ "elements": [ { "node": "Binary", - "value": "cluster.web.petersaxton.uk" + "value": "localhost:5002" }, { "node": "Variable", @@ -4587,11 +4166,11 @@ "elements": [ { "node": "Binary", - "value": "localhost:5001" + "value": "cluster.web.petersaxton.uk" }, { "node": "Variable", - "label": "dashboard" + "label": "cluster" } ] }, @@ -4619,7 +4198,7 @@ "elements": [ { "node": "Binary", - "value": "dashboard.web.petersaxton.uk" + "value": "localhost:5001" }, { "node": "Variable", @@ -4638,92 +4217,125 @@ "node": "Let", "pattern": { "node": "Variable", - "label": "route" + "label": "routes" }, "value": { - "node": "Call", - "function": { - "node": "Access", - "value": { - "node": "Access", - "value": { - "node": "Variable", - "label": "lib" - }, - "key": "list" - }, - "key": "key_find" - }, - "with": { + "node": "Tagged", + "tag": "Cons", + "value": { "node": "Tuple", "elements": [ { - "node": "Variable", - "label": "routes" + "node": "Tuple", + "elements": [ + { + "node": "Binary", + "value": "dashboard.web.petersaxton.uk" + }, + { + "node": "Variable", + "label": "dashboard" + } + ] }, { - "node": "Access", - "value": { - "node": "Variable", - "label": "request" - }, - "key": "origin" + "node": "Variable", + "label": "routes" } ] } }, "then": { - "node": "Case", - "value": { + "node": "Let", + "pattern": { "node": "Variable", "label": "route" }, - "branches": [ - { - "node": "Branch", - "name": "Error", - "pattern": { - "node": "Tuple", - "elements": [] - }, - "then": { + "value": { + "node": "Call", + "function": { + "node": "Access", + "value": { "node": "Access", "value": { "node": "Variable", - "label": "request" + "label": "lib" }, - "key": "origin" - } + "key": "list" + }, + "key": "key_find" }, - { - "node": "Branch", - "name": "Ok", - "pattern": { - "node": "Tuple", - "elements": [ - { - "node": "Bind", - "label": "" + "with": { + "node": "Tuple", + "elements": [ + { + "node": "Variable", + "label": "routes" + }, + { + "node": "Access", + "value": { + "node": "Variable", + "label": "request" }, - { - "node": "Bind", - "label": "page" - } - ] + "key": "origin" + } + ] + } + }, + "then": { + "node": "Case", + "value": { + "node": "Variable", + "label": "route" + }, + "branches": [ + { + "node": "Branch", + "name": "Error", + "pattern": { + "node": "Tuple", + "elements": [] + }, + "then": { + "node": "Access", + "value": { + "node": "Variable", + "label": "request" + }, + "key": "origin" + } }, - "then": { - "node": "Call", - "function": { - "node": "Variable", - "label": "page" + { + "node": "Branch", + "name": "Ok", + "pattern": { + "node": "Tuple", + "elements": [ + { + "node": "Bind", + "label": "" + }, + { + "node": "Bind", + "label": "page" + } + ] }, - "with": { - "node": "Variable", - "label": "request" + "then": { + "node": "Call", + "function": { + "node": "Variable", + "label": "page" + }, + "with": { + "node": "Variable", + "label": "request" + } } } - } - ] + ] + } } } } @@ -4735,25 +4347,89 @@ } }, "then": { - "node": "Record", - "fields": [ - { - "node": "Field", - "key": "proxy", - "value": { - "node": "Variable", - "label": "web" + "node": "Let", + "pattern": { + "node": "Variable", + "label": "counter_init" + }, + "value": { + "node": "Tuple", + "elements": [ + { + "node": "Access", + "value": { + "node": "Access", + "value": { + "node": "Access", + "value": { + "node": "Variable", + "label": "reload" + }, + "key": "counter" + }, + "key": "init" + }, + "key": "page" + }, + { + "node": "Access", + "value": { + "node": "Access", + "value": { + "node": "Access", + "value": { + "node": "Variable", + "label": "reload" + }, + "key": "counter" + }, + "key": "init" + }, + "key": "interrupt" } - }, - { - "node": "Field", - "key": "web", - "value": { - "node": "Variable", - "label": "web" + ] + }, + "then": { + "node": "Record", + "fields": [ + { + "node": "Field", + "key": "proxy", + "value": { + "node": "Variable", + "label": "web" + } + }, + { + "node": "Field", + "key": "web", + "value": { + "node": "Variable", + "label": "web" + } + }, + { + "node": "Field", + "key": "counter_init", + "value": { + "node": "Variable", + "label": "counter_init" + } + }, + { + "node": "Field", + "key": "counter", + "value": { + "node": "Access", + "value": { + "node": "Variable", + "label": "reload" + }, + "key": "counter" + } } - } - ] + ] + } } } } diff --git a/editor/rollup.config.js b/editor/rollup.config.js index e8965ec8d..4fd6acd31 100644 --- a/editor/rollup.config.js +++ b/editor/rollup.config.js @@ -1,76 +1,93 @@ -import svelte from 'rollup-plugin-svelte'; -import commonjs from '@rollup/plugin-commonjs'; -import resolve from '@rollup/plugin-node-resolve'; -import livereload from 'rollup-plugin-livereload'; -import { terser } from 'rollup-plugin-terser'; -import css from 'rollup-plugin-css-only'; +import svelte from "rollup-plugin-svelte"; +import commonjs from "@rollup/plugin-commonjs"; +import resolve from "@rollup/plugin-node-resolve"; +import livereload from "rollup-plugin-livereload"; +import { terser } from "rollup-plugin-terser"; +import css from "rollup-plugin-css-only"; +import json from "@rollup/plugin-json"; const production = !process.env.ROLLUP_WATCH; function serve() { - let server; + let server; - function toExit() { - if (server) server.kill(0); - } + function toExit() { + if (server) server.kill(0); + } - return { - writeBundle() { - if (server) return; - server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], { - stdio: ['ignore', 'inherit', 'inherit'], - shell: true - }); + return { + writeBundle() { + if (server) return; + server = require("child_process").spawn( + "npm", + ["run", "start", "--", "--dev"], + { + stdio: ["ignore", "inherit", "inherit"], + shell: true, + } + ); - process.on('SIGTERM', toExit); - process.on('exit', toExit); - } - }; + process.on("SIGTERM", toExit); + process.on("exit", toExit); + }, + }; } -export default { - input: 'src/main.js', - output: { - sourcemap: true, - format: 'iife', - name: 'app', - file: 'public/build/bundle.js' - }, - plugins: [ - svelte({ - compilerOptions: { - // enable run-time checks when not in production - dev: !production - } - }), - // we'll extract any component CSS out into - // a separate file - better for performance - css({ output: 'bundle.css' }), +export default [ + { + input: "src/other.js", + output: { + sourcemap: true, + format: "iife", + name: "app", + file: "public/build/other.js", + }, + plugins: [json()], + }, + { + input: "src/main.js", + output: { + sourcemap: true, + format: "iife", + name: "app", + file: "public/build/bundle.js", + }, + plugins: [ + svelte({ + compilerOptions: { + // enable run-time checks when not in production + dev: !production, + }, + }), + // we'll extract any component CSS out into + // a separate file - better for performance + css({ output: "bundle.css" }), - // If you have external dependencies installed from - // npm, you'll most likely need these plugins. In - // some cases you'll need additional configuration - - // consult the documentation for details: - // https://github.com/rollup/plugins/tree/master/packages/commonjs - resolve({ - browser: true, - dedupe: ['svelte'] - }), - commonjs(), + // If you have external dependencies installed from + // npm, you'll most likely need these plugins. In + // some cases you'll need additional configuration - + // consult the documentation for details: + // https://github.com/rollup/plugins/tree/master/packages/commonjs + resolve({ + browser: true, + dedupe: ["svelte"], + }), + commonjs(), - // In dev mode, call `npm run start` once - // the bundle has been generated - !production && serve(), + // In dev mode, call `npm run start` once + // the bundle has been generated + !production && serve(), - // Watch the `public` directory and refresh the - // browser on changes when not in production - !production && livereload('public'), + // Watch the `public` directory and refresh the + // browser on changes when not in production + !production && livereload("public"), - // If we're building for production (npm run build - // instead of npm run dev), minify - production && terser() - ], - watch: { - clearScreen: false - } -}; + // If we're building for production (npm run build + // instead of npm run dev), minify + production && terser(), + ], + watch: { + clearScreen: false, + }, + }, +]; diff --git a/editor/src/main.js b/editor/src/main.js index fdc47ab57..e382ae50f 100644 --- a/editor/src/main.js +++ b/editor/src/main.js @@ -1,4 +1,5 @@ // import { main } from "../../eyg/build/dev/javascript/eyg/dist/eyg/workspace/main"; +// TODO remove deploy import { deploy } from "../../eyg/build/dev/javascript/eyg/dist/eyg/workspace/ui.mjs"; import * as Spreasheet from "../../eyg/build/dev/javascript/eyg/dist/spreadsheet/main.mjs"; diff --git a/editor/src/other.js b/editor/src/other.js new file mode 100644 index 000000000..af24cd3a3 --- /dev/null +++ b/editor/src/other.js @@ -0,0 +1,19 @@ +import * as Entry from "../../eyg/build/dev/javascript/eyg/dist/eyg/entry.mjs"; +import * as Encode from "../../eyg/build/dev/javascript/eyg/dist/eyg/ast/encode.mjs"; + +// Roll up adds a default object that breaks matchings +import data from "../public/saved.json"; +// This is not generic has because we have the server or maybe not. this is the arbitrary script pull +// let target = new URL(document.currentScript.src).hash.slice(1); +// console.log(target, Analysis); + +(async function name() { + const source = Encode.from_json(data); + const initial = Entry.interpret_client(source, "counter"); + console.log(initial); + const { default: next } = await import("../public/saved.json"); + console.log(Encode.from_json(data)); +})(); + +// Started doing this because the code gen was troublesome, also in code gen we will nee to pull in the loader +// here we have can use the already existing gleam .run diff --git a/eyg/src/eyg/analysis.gleam b/eyg/src/eyg/analysis.gleam index d267fb7d7..b1fe51926 100644 --- a/eyg/src/eyg/analysis.gleam +++ b/eyg/src/eyg/analysis.gleam @@ -16,6 +16,10 @@ pub fn infer(untyped, type_, variables) { typer.infer(untyped, type_, t.empty, state) } +pub fn infer_unbound(untyped) { + infer(untyped, t.Unbound(-1), []) +} + pub fn infer_effectful(untyped, type_, effects, variables) { let checker = typer.init() let scope = typer.root_scope(variables) diff --git a/eyg/src/eyg/ast/encode.gleam b/eyg/src/eyg/ast/encode.gleam index 7172175e7..9dfa2dfb3 100644 --- a/eyg/src/eyg/ast/encode.gleam +++ b/eyg/src/eyg/ast/encode.gleam @@ -164,6 +164,9 @@ external fn from_array(value: JSON) -> List(JSON) = pub fn from_json(json: JSON) { assert Ok(#(node, rest)) = list.key_pop(entries(json), "node") + + // TODO need to load out properly + // TODO make this robust no asserts or assumption of order use key pop // find node and order rest case assert_string(node) { "Binary" -> { @@ -203,8 +206,8 @@ pub fn from_json(json: JSON) { let [#("label", label)] = rest ast.variable(assert_string(label)) } - "Let" -> { + let [#("pattern", pattern), #("value", value), #("then", then)] = rest let pattern = pattern_from_json(pattern) let value = from_json(value) diff --git a/eyg/src/eyg/entry.gleam b/eyg/src/eyg/entry.gleam new file mode 100644 index 000000000..1a3532bf5 --- /dev/null +++ b/eyg/src/eyg/entry.gleam @@ -0,0 +1,46 @@ +import gleam/io +import gleam/string +import eyg/ast/encode +import eyg/ast/expression as e +import eyg/interpreter/effectful +// TODO need an effectful version that allows us to access stuff here +import eyg/interpreter/tail_call +import gleam/javascript/array +import eyg/interpreter/interpreter as r +import eyg/analysis +import eyg/typer +import eyg/typer/monotype as t +import eyg/editor/editor + +fn update(page, interrupt, display, on_click) { + + io.debug(page) + display(page) + + } + + +fn b(args) { + Ok(r.Binary("done")) + } +// uses default builtin that need moving out of effectful +// has an entry point key should eventually be a hash +// maybe rename interpret standard +// builtin is always the same but env things are passed in +// All the runtime stuff is in gleam terms +// TODO are there any gleam helpers to turn tuples into things +pub fn interpret_client(source, key, display, on_click) { + io.debug("hooo") + let init = e.access(e.access(source, key), "init") + io.debug(init) + let #(typed, typer) = analysis.infer(init, t.Unbound(-1), []) + io.debug("---- typed") + let #(xtyped, typer) = typer.expand_providers(typed, typer, []) +// assert Ok(term) = effectful.eval(editor.untype(xtyped)) +// io.debug(term) + |> io.debug +// effectful.eval_call(r.BuiltinFn(b), term, effectful.real_log) +// |> io.debug +// // TODO make an AST the requires rendering +// term +} \ No newline at end of file diff --git a/eyg/src/eyg/interpreter/effectful.gleam b/eyg/src/eyg/interpreter/effectful.gleam index 87a302b4b..9d70e5a65 100644 --- a/eyg/src/eyg/interpreter/effectful.gleam +++ b/eyg/src/eyg/interpreter/effectful.gleam @@ -81,7 +81,22 @@ fn term_serialize(term) { "({ on_click: (f) => { document.onclick = () => f() }, display: (value) => document.body.innerHTML = value, + // TODO does with work can we have outside poller + on_code: (f) => { document.oncode = f } });", +// This didn't work because of rendering captured variables for a fn within some scope. +// We need a whole review of rendering or use interpreter +// "(({init}) => { +// console.log(init, 'initial state') +// const update = ({page, interrupt}) => { +// document.body.innerHTML = page +// document.onclick = () => update(interrupt({Click: 'key'})) +// document.oncode = (code) => update(interrupt({Code: code})) +// TODO this needs the codegen part of loader +// } +// update(init) +// // TODO set interval +// })" ) let page = string.concat([""]) @@ -89,6 +104,7 @@ fn term_serialize(term) { Ok(r.Binary(page)) } + fn env() { map.new() |> map.insert("do", r.BuiltinFn(do)) diff --git a/eyg/src/eyg/interpreter/interpreter.gleam b/eyg/src/eyg/interpreter/interpreter.gleam index 70bda68d1..28dadb2ef 100644 --- a/eyg/src/eyg/interpreter/interpreter.gleam +++ b/eyg/src/eyg/interpreter/interpreter.gleam @@ -64,6 +64,7 @@ pub fn render_var(assignment) { #("equal", _) -> "let equal = ([a, b]) => a == b;" // TODO remove duplication of this builtincode // can i import * as builtin from /gleam/version + // TODO builtin needs to include render #("builtin", _) -> "let builtin = {append: ([a, b]) => a + b}" // TODO have a standard builtin to lookup table #("send", BuiltinFn(_)) -> @@ -117,7 +118,8 @@ pub fn render_object(object) { string.concat(["{", tag, ":", render_object(value), "}"]) // Builtins should never be included, I need to check variables used in a previous step // Function(_,_,_,_) -> todo("this needs compile again but I need a way to do this without another type check") - Function(pattern, body, _, _) -> { + Function(pattern, body, captured, _) -> { + // TODO this needs to render captured to be useful let #(typed, typer) = analysis.infer(e.function(pattern, body), t.Unbound(-1), []) let #(typed, typer) = typer.expand_providers(typed, typer, []) javascript.render_to_string(typed, typer) From afa73944fd8b77d546545d149c7d981555fb615e Mon Sep 17 00:00:00 2001 From: Peter Date: Mon, 10 Oct 2022 18:58:36 +0200 Subject: [PATCH 2/4] formatting --- eyg/src/eyg/ast/encode.gleam | 1 - eyg/src/eyg/codegen/javascript.gleam | 3 +- eyg/src/eyg/entry.gleam | 41 +++++++++++------------ eyg/src/eyg/interpreter/effectful.gleam | 27 +++++++-------- eyg/src/eyg/interpreter/interpreter.gleam | 13 +++---- eyg/src/eyg/typer/monotype.gleam | 11 ++++-- eyg/src/platform/browser.gleam | 14 ++++---- 7 files changed, 57 insertions(+), 53 deletions(-) diff --git a/eyg/src/eyg/ast/encode.gleam b/eyg/src/eyg/ast/encode.gleam index 9dfa2dfb3..98eb02df3 100644 --- a/eyg/src/eyg/ast/encode.gleam +++ b/eyg/src/eyg/ast/encode.gleam @@ -207,7 +207,6 @@ pub fn from_json(json: JSON) { ast.variable(assert_string(label)) } "Let" -> { - let [#("pattern", pattern), #("value", value), #("then", then)] = rest let pattern = pattern_from_json(pattern) let value = from_json(value) diff --git a/eyg/src/eyg/codegen/javascript.gleam b/eyg/src/eyg/codegen/javascript.gleam index 7c783cf50..85fad8299 100644 --- a/eyg/src/eyg/codegen/javascript.gleam +++ b/eyg/src/eyg/codegen/javascript.gleam @@ -173,8 +173,7 @@ pub fn escape_string(raw) { |> string.replace("\\", "\\\\") |> string.replace("'", "\\'") |> string.replace("\"", "\\\"") - -// Not js but browser + // Not js but browser |> string.replace("<", "\\<") |> string.replace(">", "\\>") } diff --git a/eyg/src/eyg/entry.gleam b/eyg/src/eyg/entry.gleam index 1a3532bf5..4c2d75d70 100644 --- a/eyg/src/eyg/entry.gleam +++ b/eyg/src/eyg/entry.gleam @@ -12,17 +12,15 @@ import eyg/typer import eyg/typer/monotype as t import eyg/editor/editor -fn update(page, interrupt, display, on_click) { - - io.debug(page) - display(page) +fn update(page, interrupt, display, on_click) { + io.debug(page) + display(page) +} - } +fn b(args) { + Ok(r.Binary("done")) +} - -fn b(args) { - Ok(r.Binary("done")) - } // uses default builtin that need moving out of effectful // has an entry point key should eventually be a hash // maybe rename interpret standard @@ -30,17 +28,18 @@ fn b(args) { // All the runtime stuff is in gleam terms // TODO are there any gleam helpers to turn tuples into things pub fn interpret_client(source, key, display, on_click) { - io.debug("hooo") - let init = e.access(e.access(source, key), "init") - io.debug(init) + io.debug("hooo") + let init = e.access(e.access(source, key), "init") + io.debug(init) let #(typed, typer) = analysis.infer(init, t.Unbound(-1), []) io.debug("---- typed") - let #(xtyped, typer) = typer.expand_providers(typed, typer, []) -// assert Ok(term) = effectful.eval(editor.untype(xtyped)) -// io.debug(term) - |> io.debug -// effectful.eval_call(r.BuiltinFn(b), term, effectful.real_log) -// |> io.debug -// // TODO make an AST the requires rendering -// term -} \ No newline at end of file + let #(xtyped, typer) = + typer.expand_providers(typed, typer, []) + // assert Ok(term) = effectful.eval(editor.untype(xtyped)) + // io.debug(term) + |> io.debug + // effectful.eval_call(r.BuiltinFn(b), term, effectful.real_log) + // |> io.debug + // // TODO make an AST the requires rendering + // term +} diff --git a/eyg/src/eyg/interpreter/effectful.gleam b/eyg/src/eyg/interpreter/effectful.gleam index 9d70e5a65..f91854a65 100644 --- a/eyg/src/eyg/interpreter/effectful.gleam +++ b/eyg/src/eyg/interpreter/effectful.gleam @@ -84,27 +84,26 @@ fn term_serialize(term) { // TODO does with work can we have outside poller on_code: (f) => { document.oncode = f } });", -// This didn't work because of rendering captured variables for a fn within some scope. -// We need a whole review of rendering or use interpreter -// "(({init}) => { -// console.log(init, 'initial state') -// const update = ({page, interrupt}) => { -// document.body.innerHTML = page -// document.onclick = () => update(interrupt({Click: 'key'})) -// document.oncode = (code) => update(interrupt({Code: code})) -// TODO this needs the codegen part of loader -// } -// update(init) -// // TODO set interval -// })" ) + // This didn't work because of rendering captured variables for a fn within some scope. + // We need a whole review of rendering or use interpreter + // "(({init}) => { + // console.log(init, 'initial state') + // const update = ({page, interrupt}) => { + // document.body.innerHTML = page + // document.onclick = () => update(interrupt({Click: 'key'})) + // document.oncode = (code) => update(interrupt({Code: code})) + // TODO this needs the codegen part of loader + // } + // update(init) + // // TODO set interval + // })" let page = string.concat([""]) // assert r.Function() = term Ok(r.Binary(page)) } - fn env() { map.new() |> map.insert("do", r.BuiltinFn(do)) diff --git a/eyg/src/eyg/interpreter/interpreter.gleam b/eyg/src/eyg/interpreter/interpreter.gleam index 28dadb2ef..a10abcf18 100644 --- a/eyg/src/eyg/interpreter/interpreter.gleam +++ b/eyg/src/eyg/interpreter/interpreter.gleam @@ -12,7 +12,6 @@ import eyg/analysis import eyg/typer import eyg/typer/monotype as t - pub type Object { Binary(String) Pid(Int) @@ -119,12 +118,14 @@ pub fn render_object(object) { // Builtins should never be included, I need to check variables used in a previous step // Function(_,_,_,_) -> todo("this needs compile again but I need a way to do this without another type check") Function(pattern, body, captured, _) -> { - // TODO this needs to render captured to be useful - let #(typed, typer) = analysis.infer(e.function(pattern, body), t.Unbound(-1), []) - let #(typed, typer) = typer.expand_providers(typed, typer, []) - javascript.render_to_string(typed, typer) + // TODO this needs to render captured to be useful + let #(typed, typer) = + analysis.infer(e.function(pattern, body), t.Unbound(-1), []) + let #(typed, typer) = typer.expand_providers(typed, typer, []) + javascript.render_to_string(typed, typer) } - BuiltinFn(_) -> "null /* we aren't using builtin here should be part of env */" + BuiltinFn(_) -> + "null /* we aren't using builtin here should be part of env */" // TODO remove Coroutine/ready there where and old experiment Coroutine(_) -> "null" Ready(_, _) -> "null" diff --git a/eyg/src/eyg/typer/monotype.gleam b/eyg/src/eyg/typer/monotype.gleam index 53c8d7c17..0e1ae6aeb 100644 --- a/eyg/src/eyg/typer/monotype.gleam +++ b/eyg/src/eyg/typer/monotype.gleam @@ -76,8 +76,15 @@ pub fn literal(monotype) { // need to add effects string.concat(["new T.Function(", literal(from), ",", literal(to), ")"]) Unbound(i) -> string.concat(["new T.Unbound(", int.to_string(i), ")"]) - Recursive(i, rest) -> string.concat(["new T.Recursive(", int.to_string(i), ", ", literal(rest), ")"]) - Native(_, _) -> { + Recursive(i, rest) -> + string.concat([ + "new T.Recursive(", + int.to_string(i), + ", ", + literal(rest), + ")", + ]) + Native(_, _) -> { io.debug(monotype) todo("ss literal") } diff --git a/eyg/src/platform/browser.gleam b/eyg/src/platform/browser.gleam index a92e1d4ca..3a08250d0 100644 --- a/eyg/src/platform/browser.gleam +++ b/eyg/src/platform/browser.gleam @@ -36,13 +36,13 @@ fn builtins() { ) |> add_field("serialize", t.Function(t.Unbound(0), t.Binary, t.empty)) |> add_field( - "compile", - t.Function( - t.Tuple([t.Binary, t.Binary]), - t.Union([#("OK", t.Unbound(0)), #("Error", t.Binary)], Some(1)), - t.empty, - ), - ) + "compile", + t.Function( + t.Tuple([t.Binary, t.Binary]), + t.Union([#("OK", t.Unbound(0)), #("Error", t.Binary)], Some(1)), + t.empty, + ), + ) } fn add_field(state, key, type_) { From d97e121e38863fa8818322bfff6d102d3c1650e3 Mon Sep 17 00:00:00 2001 From: Peter Date: Mon, 10 Oct 2022 19:25:57 +0200 Subject: [PATCH 3/4] some more tweeks --- editor/public/saved.json | 22 ++++++++++++++++++++-- editor/src/other.js | 9 ++++++--- eyg/src/eyg/entry.gleam | 20 +++++++++++++++----- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/editor/public/saved.json b/editor/public/saved.json index ffa20a566..6f5a56563 100644 --- a/editor/public/saved.json +++ b/editor/public/saved.json @@ -3057,8 +3057,26 @@ "elements": [] }, "then": { - "node": "Binary", - "value": "TODO encode" + "node": "Call", + "function": { + "node": "Access", + "value": { + "node": "Variable", + "label": "builtin" + }, + "key": "serialize" + }, + "with": { + "node": "Function", + "pattern": { + "node": "Variable", + "label": "" + }, + "body": { + "node": "Tuple", + "elements": [] + } + } } } ] diff --git a/editor/src/other.js b/editor/src/other.js index af24cd3a3..382cbe7e0 100644 --- a/editor/src/other.js +++ b/editor/src/other.js @@ -7,13 +7,16 @@ import data from "../public/saved.json"; // let target = new URL(document.currentScript.src).hash.slice(1); // console.log(target, Analysis); -(async function name() { +async function GoGoGo() { const source = Encode.from_json(data); const initial = Entry.interpret_client(source, "counter"); console.log(initial); const { default: next } = await import("../public/saved.json"); - console.log(Encode.from_json(data)); -})(); + console.log(Encode.from_json(next)); +} +console.log(window, GoGoGo); +window.GoGoGo = GoGoGo; +GoGoGo(); // Started doing this because the code gen was troublesome, also in code gen we will nee to pull in the loader // here we have can use the already existing gleam .run diff --git a/eyg/src/eyg/entry.gleam b/eyg/src/eyg/entry.gleam index 4c2d75d70..eeb268a9a 100644 --- a/eyg/src/eyg/entry.gleam +++ b/eyg/src/eyg/entry.gleam @@ -1,4 +1,6 @@ import gleam/io +import gleam/map +import gleam/option.{None} import gleam/string import eyg/ast/encode import eyg/ast/expression as e @@ -7,20 +9,26 @@ import eyg/interpreter/effectful import eyg/interpreter/tail_call import gleam/javascript/array import eyg/interpreter/interpreter as r +import eyg/ast/pattern as p import eyg/analysis import eyg/typer import eyg/typer/monotype as t import eyg/editor/editor + fn update(page, interrupt, display, on_click) { io.debug(page) display(page) } fn b(args) { - Ok(r.Binary("done")) + Ok(args) } +fn tree() { + r.Function(p.Variable("x"), e.tuple_([e.variable("x"),e.variable("x")]), map.new(), None) + } + // uses default builtin that need moving out of effectful // has an entry point key should eventually be a hash // maybe rename interpret standard @@ -35,11 +43,13 @@ pub fn interpret_client(source, key, display, on_click) { io.debug("---- typed") let #(xtyped, typer) = typer.expand_providers(typed, typer, []) - // assert Ok(term) = effectful.eval(editor.untype(xtyped)) - // io.debug(term) + // Running the interpreter kills the client + // assert Ok(term) = effectful.eval(editor.untype(xtyped)) + // io.debug(term) + |> io.debug + io.debug("expanded") + effectful.eval_call(tree(), r.Binary("nothing exciting"), effectful.real_log) |> io.debug - // effectful.eval_call(r.BuiltinFn(b), term, effectful.real_log) - // |> io.debug // // TODO make an AST the requires rendering // term } From b695ad75fa299bfc068d78a3369930cdb0043af5 Mon Sep 17 00:00:00 2001 From: Peter Date: Mon, 10 Oct 2022 20:18:58 +0200 Subject: [PATCH 4/4] try a tree shaking algorithm --- eyg/src/eyg/entry.gleam | 20 +++-- eyg/test/eyg/analysis_test.gleam | 130 +++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 8 deletions(-) create mode 100644 eyg/test/eyg/analysis_test.gleam diff --git a/eyg/src/eyg/entry.gleam b/eyg/src/eyg/entry.gleam index eeb268a9a..776974bbe 100644 --- a/eyg/src/eyg/entry.gleam +++ b/eyg/src/eyg/entry.gleam @@ -15,7 +15,6 @@ import eyg/typer import eyg/typer/monotype as t import eyg/editor/editor - fn update(page, interrupt, display, on_click) { io.debug(page) display(page) @@ -25,9 +24,14 @@ fn b(args) { Ok(args) } -fn tree() { - r.Function(p.Variable("x"), e.tuple_([e.variable("x"),e.variable("x")]), map.new(), None) - } +fn tree() { + r.Function( + p.Variable("x"), + e.tuple_([e.variable("x"), e.variable("x")]), + map.new(), + None, + ) +} // uses default builtin that need moving out of effectful // has an entry point key should eventually be a hash @@ -44,12 +48,12 @@ pub fn interpret_client(source, key, display, on_click) { let #(xtyped, typer) = typer.expand_providers(typed, typer, []) // Running the interpreter kills the client - // assert Ok(term) = effectful.eval(editor.untype(xtyped)) - // io.debug(term) + // assert Ok(term) = effectful.eval(editor.untype(xtyped)) + // io.debug(term) |> io.debug io.debug("expanded") - effectful.eval_call(tree(), r.Binary("nothing exciting"), effectful.real_log) - |> io.debug + effectful.eval_call(tree(), r.Binary("nothing exciting"), effectful.real_log) + |> io.debug // // TODO make an AST the requires rendering // term } diff --git a/eyg/test/eyg/analysis_test.gleam b/eyg/test/eyg/analysis_test.gleam new file mode 100644 index 000000000..456f0ebed --- /dev/null +++ b/eyg/test/eyg/analysis_test.gleam @@ -0,0 +1,130 @@ +import gleam/io +import gleam/option.{Some} +import gleam/list +import gleam/string +import eyg/ast/expression as e +import eyg/ast/pattern as p +import eyg/typer/monotype as t +import eyg/analysis +import eyg/typer + +fn variables_needed(source) { + do_variables_needed(source) + |> list.unique + |> list.sort(string.compare) +} + +fn do_variables_needed(tree) { + let #(_, exp) = tree + case exp { + e.Binary(_) | e.Hole -> [] + e.Variable(label) -> [label] + e.Tuple(elems) -> + list.fold( + elems, + [], + fn(acc, e) { list.append(acc, do_variables_needed(e)) }, + ) + e.Record(fields) -> + list.fold( + fields, + [], + fn(acc, f) { + let #(_, e) = f + list.append(acc, do_variables_needed(e)) + }, + ) + e.Access(e, _) | e.Tagged(_, e) -> do_variables_needed(e) + e.Call(f, arg) -> + list.append(do_variables_needed(f), do_variables_needed(arg)) + e.Let(_p, value, then) -> + list.append(do_variables_needed(value), do_variables_needed(then)) + // TODO pattern + e.Function(_p, body) -> do_variables_needed(body) + e.Case(value, branches) -> + list.fold( + branches, + do_variables_needed(value), + fn(acc, b) { + // TODO remove pattern + let #(_, _, then) = b + list.append(acc, do_variables_needed(then)) + }, + ) + // TODO these shouldn't add anything new or we will change them to be eval'd functions + e.Provider(_, _, _) -> [] + } + // _ -> todo +} + +fn by_type(tree, scope) { + assert Ok(typer) = do_type(tree, scope, typer.init()) + t.resolve(scope, typer.substitutions) +} + +fn do_type(tree, scope, state) -> Result(typer.Typer, _) { + let #(_, exp) = tree + case exp { + e.Variable(label) -> { + let #(row, state) = typer.next_unbound(state) + let #(type_, state) = typer.next_unbound(state) + typer.unify( + t.Record([#(label, t.Unbound(type_))], Some(row)), + scope, + state, + ) + } + e.Let(_, value, then) -> do_type(value, scope, state) + e.Access(term, key) -> + // This is on the type of the term does nothing to scope. I think need to run through checking the typer + todo + _ -> { + io.debug(exp) + todo + } + } +} + +pub fn slim_records_test() -> Nil { + // e.let_(p.Record([]), e.record([]), e.hole()) + + let source = e.variable("x") + assert _ = + by_type(source, t.Unbound(-1)) + |> io.debug + + let source = + e.let_( + p.Variable(""), + e.access(e.variable("x"), "foo"), + e.let_(p.Variable(""), e.access(e.variable("x"), "foo"), e.hole()), + ) + + assert _ = + by_type(source, t.Unbound(-1)) + |> io.debug + todo("slim") +} + +pub fn capturing_all_type_variables_test() { + // If there is a function from string -> string could be a -> b a -> string b -> string + // how do providers work over this + let source = e.variable("x") + assert ["x"] = variables_needed(source) + + let source = e.binary("hello") + assert [] = variables_needed(source) + + let source = e.call(e.variable("x"), e.variable("y")) + assert ["x", "y"] = variables_needed(source) + + let source = e.tuple_([e.variable("x"), e.variable("y")]) + assert ["x", "y"] = variables_needed(source) + + let source = e.access(e.variable("x"), "y") + assert ["x"] = variables_needed(source) + + let source = e.tagged("Y", e.variable("x")) + assert ["x"] = variables_needed(source) + // todo("test") +}