diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4e459652..2f344116 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,9 +12,13 @@ updates: commit-message: prefix: "⬆️" open-pull-requests-limit: 100 + labels: + - "dependencies" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" commit-message: prefix: "⬆️" + labels: + - "dependencies" diff --git a/.github/workflows/ci-cachix.yml b/.github/workflows/ci-cachix.yml new file mode 100644 index 00000000..dfcee3d8 --- /dev/null +++ b/.github/workflows/ci-cachix.yml @@ -0,0 +1,87 @@ +name: Nix build + +on: + push: + branches: + - master + - develop + pull_request: + branches: + - master + - develop + - reducer-dev + - epic-reducer-project + +jobs: + flake-lints: + name: All lint + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Install nix + uses: cachix/install-nix-action@v17 + with: + nix_path: nixpkgs=channel:nixos-22.05 + - name: Use cachix + uses: cachix/cachix-action@v10 + with: + name: quantified-uncertainty + authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" + + - name: Check that lang lints + run: nix build .#lang-lint + - name: Check that components lints + run: nix build .#components-lint + - name: Check that website lints + run: nix build .#docusaurus-lint + - name: Check that vscode extension lints + run: nix build .#vscode-lint + - name: Check that cli lints + run: nix build .#cli-lint + + flake-packages: + name: Builds, tests, and bundles + runs-on: ubuntu-latest + needs: flake-lints + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Install nix + uses: cachix/install-nix-action@v17 + with: + nix_path: nixpkgs=channel:nixos-22.05 + - name: Use cachix + uses: cachix/cachix-action@v10 + with: + name: quantified-uncertainty + authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" + + - name: Check all lang tests + run: nix build .#lang-test + - name: Check that lang bundles + run: nix build .#lang-bundle + - name: Check that components builds + run: nix build .#components + - name: Check that components bundles + run: nix build .#components-bundle + + flake-devshells: + name: Development shell environment + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Install nix + uses: cachix/install-nix-action@v17 + with: + nix_path: nixpkgs=channel:nixos-22.05 + - name: Use cachix + uses: cachix/cachix-action@v10 + with: + name: quantified-uncertainty + authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" + - name: Build js devshell + run: nix develop .#js --profile just-js + - name: Build js & wasm devshell + run: nix develop --profile full-shell diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8150d00a..a2d0ef16 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,7 @@ on: - master - develop - reducer-dev + - epic-reducer-project jobs: pre_check: @@ -48,26 +49,26 @@ jobs: with: paths: '["packages/cli/**"]' - lang-lint: - name: Language lint - runs-on: ubuntu-latest - needs: pre_check - if: ${{ needs.pre_check.outputs.should_skip_lang != 'true' }} - defaults: - run: - shell: bash - working-directory: packages/squiggle-lang - steps: - - uses: actions/checkout@v3 - - name: Install Dependencies - run: cd ../../ && yarn - - name: Check rescript lint - run: yarn lint:rescript - - name: Check javascript, typescript, and markdown lint - uses: creyD/prettier_action@v4.2 - with: - dry: true - prettier_options: --check packages/squiggle-lang + # lang-lint: + # name: Language lint + # runs-on: ubuntu-latest + # needs: pre_check + # if: ${{ needs.pre_check.outputs.should_skip_lang != 'true' }} + # defaults: + # run: + # shell: bash + # working-directory: packages/squiggle-lang + # steps: + # - uses: actions/checkout@v3 + # - name: Install Dependencies + # run: cd ../../ && yarn + # - name: Check rescript lint + # run: yarn lint:rescript + # - name: Check javascript, typescript, and markdown lint + # uses: creyD/prettier_action@v4.2 + # with: + # dry: true + # prettier_options: --check packages/squiggle-lang lang-build-test-bundle: name: Language build, test, and bundle @@ -97,95 +98,96 @@ jobs: - name: Upload typescript coverage report run: yarn coverage:ts:ci - components-lint: - name: Components lint - runs-on: ubuntu-latest - needs: pre_check - if: ${{ needs.pre_check.outputs.should_skip_components != 'true' }} - defaults: - run: - shell: bash - working-directory: packages/components - steps: - - uses: actions/checkout@v3 - - name: Check javascript, typescript, and markdown lint - uses: creyD/prettier_action@v4.2 - with: - dry: true - prettier_options: --check packages/components --ignore-path packages/components/.prettierignore + # components-lint: + # name: Components lint + # runs-on: ubuntu-latest + # needs: pre_check + # if: ${{ needs.pre_check.outputs.should_skip_components != 'true' }} + # defaults: + # run: + # shell: bash + # working-directory: packages/components + # steps: + # - uses: actions/checkout@v3 + # - name: Check javascript, typescript, and markdown lint + # uses: creyD/prettier_action@v4.2 + # with: + # dry: true + # prettier_options: --check packages/components --ignore-path packages/components/.prettierignore + # + # components-bundle-build: + # name: Components bundle and build + # runs-on: ubuntu-latest + # needs: pre_check + # if: ${{ (needs.pre_check.outputs.should_skip_components != 'true') || (needs.pre_check.outputs.should_skip_lang != 'true') }} + # defaults: + # run: + # shell: bash + # working-directory: packages/components + # steps: + # - uses: actions/checkout@v3 + # - name: Install dependencies from monorepo level + # run: cd ../../ && yarn + # - name: Build rescript codebase in squiggle-lang + # run: cd ../squiggle-lang && yarn build + # - name: Run webpack + # run: yarn bundle + # - name: Build storybook + # run: yarn build - components-bundle-build: - name: Components bundle and build - runs-on: ubuntu-latest - needs: pre_check - if: ${{ (needs.pre_check.outputs.should_skip_components != 'true') || (needs.pre_check.outputs.should_skip_lang != 'true') }} - defaults: - run: - shell: bash - working-directory: packages/components - steps: - - uses: actions/checkout@v3 - - name: Install dependencies from monorepo level - run: cd ../../ && yarn - - name: Build rescript codebase in squiggle-lang - run: cd ../squiggle-lang && yarn build - - name: Run webpack - run: yarn bundle - - name: Build storybook - run: yarn build - - website-lint: - name: Website lint - runs-on: ubuntu-latest - needs: pre_check - if: ${{ needs.pre_check.outputs.should_skip_website != 'true' }} - defaults: - run: - shell: bash - working-directory: packages/website - steps: - - uses: actions/checkout@v3 - - name: Check javascript, typescript, and markdown lint - uses: creyD/prettier_action@v4.2 - with: - dry: true - prettier_options: --check packages/website - - website-build: - name: Website build - runs-on: ubuntu-latest - needs: pre_check - if: ${{ (needs.pre_check.outputs.should_skip_website != 'true') || (needs.pre_check.outputs.should_skip_lang != 'true') || (needs.pre_check.outputs.should_skip_components != 'true') }} - defaults: - run: - shell: bash - working-directory: packages/website - steps: - - uses: actions/checkout@v3 - - name: Install dependencies from monorepo level - run: cd ../../ && yarn - - name: Build rescript in squiggle-lang - run: cd ../squiggle-lang && yarn build - - name: Build components - run: cd ../components && yarn build - - name: Build website assets - run: yarn build - - vscode-ext-lint: - name: VS Code extension lint - runs-on: ubuntu-latest - needs: pre_check - if: ${{ needs.pre_check.outputs.should_skip_vscodeext != 'true' }} - defaults: - run: - shell: bash - working-directory: packages/vscode-ext - steps: - - uses: actions/checkout@v3 - - name: Install dependencies from monorepo level - run: cd ../../ && yarn - - name: Lint the VSCode Extension source code - run: yarn lint + # website-lint: + # name: Website lint + # runs-on: ubuntu-latest + # needs: pre_check + # if: ${{ needs.pre_check.outputs.should_skip_website != 'true' }} + # defaults: + # run: + # shell: bash + # working-directory: packages/website + # steps: + # - uses: actions/checkout@v3 + # - name: Check javascript, typescript, and markdown lint + # uses: creyD/prettier_action@v4.2 + # with: + # dry: true + # prettier_options: --check packages/website + # + # website-build: + # name: Website build + # runs-on: ubuntu-latest + # needs: pre_check + # if: ${{ (needs.pre_check.outputs.should_skip_website != 'true') || (needs.pre_check.outputs.should_skip_lang != 'true') || (needs.pre_check.outputs.should_skip_components != 'true') }} + # defaults: + # run: + # shell: bash + # working-directory: packages/website + # steps: + # - uses: actions/checkout@v3 + # - name: Install dependencies from monorepo level + # run: cd ../../ && yarn + # - name: Build rescript in squiggle-lang + # run: cd ../squiggle-lang && yarn build + # - name: Build components + # run: cd ../components && yarn build + # - name: Build website assets + # run: yarn build + # + # vscode-ext-lint: + # name: VS Code extension lint + # runs-on: ubuntu-latest + # needs: pre_check + # if: ${{ needs.pre_check.outputs.should_skip_vscodeext != 'true' }} + # defaults: + # run: + # shell: bash + # working-directory: packages/vscode-ext + # steps: + # - uses: actions/checkout@v3 + # - name: Check javascript, typescript, and markdown lint + # uses: creyD/prettier_action@v4.2 + # with: + # dry: true + # prettier_options: --check packages/vscode-ext vscode-ext-build: name: VS Code extension build @@ -202,20 +204,19 @@ jobs: run: cd ../../ && yarn - name: Build run: yarn compile - - cli-lint: - name: CLI lint - runs-on: ubuntu-latest - needs: pre_check - if: ${{ needs.pre_check.outputs.should_skip_cli != 'true' }} - defaults: - run: - shell: bash - working-directory: packages/cli - steps: - - uses: actions/checkout@v3 - - name: Check javascript, typescript, and markdown lint - uses: creyD/prettier_action@v4.2 - with: - dry: true - prettier_options: --check packages/cli +# cli-lint: +# name: CLI lint +# runs-on: ubuntu-latest +# needs: pre_check +# if: ${{ needs.pre_check.outputs.should_skip_cli != 'true' }} +# defaults: +# run: +# shell: bash +# working-directory: packages/cli +# steps: +# - uses: actions/checkout@v3 +# - name: Check javascript, typescript, and markdown lint +# uses: creyD/prettier_action@v4.2 +# with: +# dry: true +# prettier_options: --check packages/cli diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index f4bbbf9e..bd3fa87b 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -3,7 +3,7 @@ name: Run Release Please on: push: branches: - - develop + - master jobs: pre_check: @@ -55,21 +55,22 @@ jobs: token: ${{secrets.GITHUB_TOKEN}} command: manifest-pr path: packages/squiggle-lang - bump-patch-for-minor-pre-major: true + # bump-patch-for-minor-pre-major: true skip-github-release: true - # - name: Publish: Checkout source - # uses: actions/checkout@v2 - # # these if statements ensure that a publication only occurs when - # # a new release is created: - # if: ${{ steps.release.outputs.release_created }} - # - name: Publish: Install dependencies - # run: yarn - # if: ${{ steps.release.outputs.release_created }} - # - name: Publish - # run: cd packages/squiggle-lang && yarn publish - # env: - # NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} - # if: ${{ steps.release.outputs.release_created }} + - name: Publish- Checkout source + uses: actions/checkout@v3 + # these if statements ensure that a publication only occurs when + # a new release is created: + if: ${{ steps.release.outputs.release_created }} + - name: Publish- Install dependencies + run: yarn + if: ${{ steps.release.outputs.release_created }} + - name: Publish + run: cd packages/squiggle-lang && yarn publish + env: + NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} + if: ${{ steps.release.outputs.release_created }} + relplz-components: name: for components runs-on: ubuntu-latest @@ -82,20 +83,20 @@ jobs: token: ${{secrets.GITHUB_TOKEN}} command: manifest-pr path: packages/components - bump-patch-for-minor-pre-major: true + # bump-patch-for-minor-pre-major: true skip-github-release: true - # - name: Publish: Checkout source - # uses: actions/checkout@v2 - # # these if statements ensure that a publication only occurs when - # # a new release is created: - # if: ${{ steps.release.outputs.release_created }} - # - name: Publish: Install dependencies - # run: yarn - # if: ${{ steps.release.outputs.release_created }} - # - name: Publish - # run: cd packages/components && yarn publish - # env: - # NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} + - name: Publish- Checkout source + uses: actions/checkout@v3 + # these if statements ensure that a publication only occurs when + # a new release is created: + if: ${{ steps.release.outputs.release_created }} + - name: Publish- Install dependencies + run: yarn + if: ${{ steps.release.outputs.release_created }} + - name: Publish + run: cd packages/components && yarn publish + env: + NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} relplz-website: name: for website runs-on: ubuntu-latest @@ -108,7 +109,7 @@ jobs: token: ${{secrets.GITHUB_TOKEN}} command: manifest-pr path: packages/website - bump-patch-for-minor-pre-major: true + # bump-patch-for-minor-pre-major: true skip-github-release: true relplz-vscodeext: name: for vscode-ext @@ -122,7 +123,7 @@ jobs: token: ${{secrets.GITHUB_TOKEN}} command: manifest-pr path: packages/vscode-ext - bump-patch-for-minor-pre-major: true + # bump-patch-for-minor-pre-major: true skip-github-release: true relplz-cl: name: for cli diff --git a/.gitignore b/.gitignore index c05ec659..8f538f09 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ yarn-error.log .log .vscode -todo.txt \ No newline at end of file +todo.txt +result diff --git a/.prettierignore b/.prettierignore index 8090b3f3..ba58d386 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,15 +1,16 @@ .direnv *.bs.js *.gen.tsx -packages/*/dist packages/components/storybook-static node_modules packages/*/node_modules packages/website/.docusaurus packages/squiggle-lang/lib -packages/squiggle-lang/.nyc_output/ packages/squiggle-lang/coverage/ packages/squiggle-lang/.cache/ packages/website/build/ packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.js packages/vscode-ext/media/vendor/ +packages/squiggle-lang/.nyc_output/ +packages/*/dist +result diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..67f81079 --- /dev/null +++ b/flake.lock @@ -0,0 +1,79 @@ +{ + "nodes": { + "flake-utils": { + "locked": { + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "locked": { + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gentype": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1661855866, + "narHash": "sha256-+q0OOTyaq8eOn9BOWdPOCtSDOISW4A59v3mq3JOZyug=", + "owner": "rescript-association", + "repo": "genType", + "rev": "6b5f164b4f6ced456019b7579a0ab7e0a86518ad", + "type": "github" + }, + "original": { + "owner": "rescript-association", + "repo": "genType", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1661617163, + "narHash": "sha256-NN9Ky47j8ohgPhA9JZyfkYIbbAo6RJkGz+7h8/exVpE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "0ba2543f8c855d7be8e90ef6c8dc89c1617e8a08", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-22.05", + "type": "indirect" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "gentype": "gentype", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..97bcf485 --- /dev/null +++ b/flake.nix @@ -0,0 +1,99 @@ +{ + description = "Squiggle packages"; + + inputs = { + nixpkgs.url = "nixpkgs/nixos-22.05"; + gentype = { + url = "github:rescript-association/genType"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, gentype, flake-utils }: + let + version = builtins.substring 0 8 self.lastModifiedDate; + overlays = [ + (final: prev: { + # set the node version here + nodejs = prev.nodejs-18_x; + # The override is the only way to get it into mkYarnModules + }) + ]; + + commonFn = pkgs: { + buildInputs = with pkgs; [ nodejs yarn ]; + prettier = with pkgs.nodePackages; [ prettier ]; + which = [ pkgs.which ]; + }; + gentypeOutputFn = pkgs: gentype.outputs.packages.${pkgs.system}.default; + langFn = { pkgs, ... }: + # Probably doesn't work on i686-linux + import ./nix/squiggle-lang.nix { + inherit pkgs commonFn gentypeOutputFn; + }; + componentsFn = { pkgs, ... }: + import ./nix/squiggle-components.nix { inherit pkgs commonFn langFn; }; + websiteFn = { pkgs, ... }: + import ./nix/squiggle-website.nix { + inherit pkgs commonFn langFn componentsFn; + }; + vscodeextFn = { pkgs, ... }: + import ./nix/squiggle-vscode.nix { + inherit pkgs commonFn langFn componentsFn; + }; + cliFn = { pkgs, ... }: + import ./nix/squiggle-cli.nix { + inherit pkgs commonFn; + }; + + # local machines + localFlakeOutputs = { pkgs, ... }: + let + lang = langFn pkgs; + components = componentsFn pkgs; + website = websiteFn pkgs; + vscodeext = vscodeextFn pkgs; + cli = cliFn pkgs; + in { + # validating + checks = flake-utils.lib.flattenTree { + lang-lint = lang.lint; + lang-test = lang.test; + components-lint = components.lint; + docusaurus-lint = website.lint; + cli-lint = cli.lint; + }; + # building + packages = flake-utils.lib.flattenTree { + default = components.build; + lang = lang.build; + lang-bundle = lang.bundle; + lang-test = lang.test; + components = components.build; + components-bundle = components.bundle; + + # Lint + lang-lint = lang.lint; + components-lint = components.lint; + docusaurus-lint = website.lint; + vscode-lint = vscodeext.lint; + cli-lint = cli.lint; + }; + + # developing + devShells = let shellNix = import ./nix/shell.nix { inherit pkgs; }; + in flake-utils.lib.flattenTree { + default = shellNix.all; + js = shellNix.just-js; + }; + }; + in flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { + inherit system; + overlays = overlays; + }; + + in localFlakeOutputs pkgs); +} diff --git a/nix/README.md b/nix/README.md new file mode 100644 index 00000000..6ad6a456 --- /dev/null +++ b/nix/README.md @@ -0,0 +1 @@ +Visit `quantified-uncertainty.cachix.org` for information about how to add our binary cache to your local dev environment. diff --git a/nix/shell.nix b/nix/shell.nix new file mode 100644 index 00000000..26f62625 --- /dev/null +++ b/nix/shell.nix @@ -0,0 +1,25 @@ +{ pkgs }: +with pkgs; +let + js = [ yarn nodejs nodePackages.ts-node ]; + rust = [ + wasm-pack + cargo + rustup + pkg-config + libressl + rustfmt + wasmtime + binaryen + wasm-bindgen-cli + ]; +in { + all = mkShell { + name = "squiggle_yarn-wasm-devshell"; + buildInputs = builtins.concatLists [ js rust [ nixfmt ] ]; + }; + just-js = mkShell { + name = "squiggle_yarn-devshell"; + buildInputs = js ++ [ nixfmt ]; + }; +} diff --git a/nix/squiggle-cli.nix b/nix/squiggle-cli.nix new file mode 100644 index 00000000..4dd7998b --- /dev/null +++ b/nix/squiggle-cli.nix @@ -0,0 +1,13 @@ +{ pkgs, commonFn }: + +rec { + common = commonFn pkgs; + + lint = pkgs.stdenv.mkDerivation { + name = "squiggle-cli-lint"; + buildInputs = common.buildInputs ++ common.prettier; + src = ../packages/cli; + buildPhase = "prettier --check ."; + installPhase = "mkdir -p $out"; + }; +} diff --git a/nix/squiggle-components.nix b/nix/squiggle-components.nix new file mode 100644 index 00000000..3a01641f --- /dev/null +++ b/nix/squiggle-components.nix @@ -0,0 +1,75 @@ +{ pkgs, commonFn, langFn }: + +rec { + common = commonFn pkgs; + lang = langFn pkgs; + componentsPackageJson = let + raw = pkgs.lib.importJSON ../packages/components/package.json; + modified = + pkgs.lib.recursiveUpdate raw { dependencies.react-dom = "^18.2.0"; }; + packageJsonString = builtins.toJSON modified; + in pkgs.writeText "packages/components/patched-package.json" + packageJsonString; + yarn-source = pkgs.mkYarnPackage { + name = "squiggle-components_yarnsource"; + buildInputs = common.buildInputs; + src = ../packages/components; + packageJSON = componentsPackageJson; + yarnLock = ../yarn.lock; + packageResolutions."@quri/squiggle-lang" = lang.build; + }; + lint = pkgs.stdenv.mkDerivation { + name = "squiggle-components-lint"; + src = ../packages/components; + buildInputs = common.buildInputs ++ common.prettier; + buildPhase = "yarn lint"; + installPhase = "mkdir -p $out"; + }; + build = pkgs.stdenv.mkDerivation { + name = "squiggle-components-build"; + src = yarn-source + "/libexec/@quri/squiggle-components"; + buildInputs = common.buildInputs; + buildPhase = '' + cp -r node_modules/@quri/squiggle-lang deps/@quri + pushd deps/@quri/squiggle-components + + yarn --offline build:cjs + yarn --offline build:css + popd + ''; + installPhase = '' + mkdir -p $out + + # annoying hack because permissions on transitive dependencies later on + mv deps/@quri/squiggle-components/node_modules deps/@quri/squiggle-components/NODE_MODULES + mv node_modules deps/@quri/squiggle-components + + # patching .gitignore so flake keeps build artefacts + sed -i /dist/d deps/@quri/squiggle-components/.gitignore + cp -r deps/@quri/squiggle-components/. $out + ''; + }; + bundle = pkgs.stdenv.mkDerivation { + name = "squiggle-components-bundle"; + src = yarn-source + "/libexec/@quri/squiggle-components"; + buildInputs = common.buildInputs; + buildPhase = '' + cp -r node_modules/@quri/squiggle-lang deps/@quri + pushd deps/@quri/squiggle-components + + yarn --offline bundle + popd + ''; + installPhase = '' + mkdir -p $out + + # annoying hack because permissions on transitive dependencies later on + mv deps/@quri/squiggle-components/node_modules deps/@quri/squiggle-components/NODE_MODULES + mv node_modules deps/@quri/squiggle-components + + # patching .gitignore so flake keeps build artefacts + sed -i /dist/d deps/@quri/squiggle-components/.gitignore + cp -r deps/@quri/squiggle-components/. $out + ''; + }; +} diff --git a/nix/squiggle-lang.nix b/nix/squiggle-lang.nix new file mode 100644 index 00000000..4d855d7f --- /dev/null +++ b/nix/squiggle-lang.nix @@ -0,0 +1,125 @@ +{ pkgs, commonFn, gentypeOutputFn }: + +rec { + common = commonFn pkgs; + langPackageJson = let + raw = pkgs.lib.importJSON ../packages/squiggle-lang/package.json; + modified = pkgs.lib.recursiveUpdate raw { + devDependencies."@types/lodash" = "^4.14.167"; + }; + packageJsonString = builtins.toJSON modified; + in pkgs.writeText "packages/squiggle-lang/patched-package.json" + packageJsonString; + yarn-source = pkgs.mkYarnPackage { + name = "squiggle-lang_yarnsource"; + src = ../packages/squiggle-lang; + packageJSON = langPackageJson; + yarnLock = ../yarn.lock; + pkgConfig = { + rescript = { + buildInputs = common.which + ++ (if pkgs.system != "i686-linux" then [ pkgs.gcc_multi ] else [ ]); + postInstall = '' + echo "PATCHELF'ING RESCRIPT EXECUTABLES (INCL NINJA)" + # Patching interpreter for linux/*.exe's + THE_LD=$(patchelf --print-interpreter $(which mkdir)) + patchelf --set-interpreter $THE_LD linux/*.exe && echo "- patched interpreter for linux/*.exe's" + + # Replacing needed shared library for linux/ninja.exe + THE_SO=$(find /nix/store/*/lib64 -name libstdc++.so.6 | head -n 1) + patchelf --replace-needed libstdc++.so.6 $THE_SO linux/ninja.exe && echo "- replaced needed for linux/ninja.exe" + ''; + }; + bisect_ppx = { + buildInputs = common.which; + postInstall = '' + echo "PATCHELF'ING BISECT_PPX EXECUTABLE" + THE_LD=$(patchelf --print-interpreter $(which mkdir)) + patchelf --set-interpreter $THE_LD bin/linux/ppx + patchelf --set-interpreter $THE_LD bin/linux/bisect-ppx-report + cp bin/linux/ppx ppx + ''; + }; + gentype = { + postInstall = '' + mv gentype.exe ELFLESS-gentype.exe + cp ${gentypeOutputFn pkgs}/src/GenType.exe gentype.exe + ''; + }; + }; + }; + lint = pkgs.stdenv.mkDerivation { + name = "squiggle-lang-lint"; + src = yarn-source + "/libexec/@quri/squiggle-lang/deps/@quri/squiggle-lang"; + buildInputs = common.buildInputs ++ common.prettier; + buildPhase = '' + yarn lint:prettier + yarn lint:rescript + ''; + installPhase = "mkdir -p $out"; + }; + build = pkgs.stdenv.mkDerivation { + name = "squiggle-lang-build"; + # `peggy` is in the `node_modules` that's adjacent to `deps`. + src = yarn-source + "/libexec/@quri/squiggle-lang"; + buildInputs = common.buildInputs; + buildPhase = '' + # so that the path to ppx doesn't need to be patched. + mv node_modules deps + + pushd deps/@quri/squiggle-lang + yarn --offline build:peggy + yarn --offline build:rescript + yarn --offline build:typescript + + # custom gitignore so that the flake keeps build artefacts + mv .gitignore GITIGNORE + sed -i /Reducer_Peggy_GeneratedParser.js/d GITIGNORE + sed -i /\*.bs.js/d GITIGNORE + sed -i /\*.gen.ts/d GITIGNORE + sed -i /\*.gen.tsx/d GITIGNORE + sed -i /\*.gen.js/d GITIGNORE + sed -i /helpers.js/d GITIGNORE + + popd + ''; + installPhase = '' + mkdir -p $out + # mkdir -p $out/node_modules + mv deps/@quri/squiggle-lang/GITIGNORE deps/@quri/squiggle-lang/.gitignore + + # annoying hack because permissions on transitive dependencies later on + mv deps/@quri/squiggle-lang/node_modules deps/@quri/squiggle-lang/NODE_MODULES + mv deps/node_modules deps/@quri/squiggle-lang + + # the proper install phase + cp -r deps/@quri/squiggle-lang/. $out + ''; + }; + test = pkgs.stdenv.mkDerivation { + name = "squiggle-lang-test"; + src = build; + buildInputs = common.buildInputs; + buildPhase = '' + yarn --offline test + ''; + installPhase = '' + mkdir -p $out + cp -r . $out + ''; + }; + bundle = pkgs.stdenv.mkDerivation { + name = "squiggle-lang-bundle"; + src = test; + buildInputs = common.buildInputs; + buildPhase = '' + yarn --offline bundle + ''; + installPhase = '' + mkdir -p $out + cp -r dist $out + cp *.json $out/dist + ''; + }; + +} diff --git a/nix/squiggle-vscode.nix b/nix/squiggle-vscode.nix new file mode 100644 index 00000000..2e725261 --- /dev/null +++ b/nix/squiggle-vscode.nix @@ -0,0 +1,24 @@ +{ pkgs, commonFn, langFn, componentsFn }: + +rec { + common = commonFn pkgs; + lang = langFn pkgs; + components = componentsFn pkgs; + + yarn-source = pkgs.mkYarnPackage { + name = "squiggle-vscodeext_yarnsource"; + src = ../packages/vscode-ext; + packageJson = ../packages/vscode-ext/package.json; + yarnLock = ../yarn.lock; + packageResolutions."@quri/squiggle-lang" = lang.build; + packageResolutions."@quri/squiggle-components" = components.build; + }; + lint = pkgs.stdenv.mkDerivation { + name = "squiggle-vscode-lint"; + buildInputs = common.buildInputs ++ common.prettier; + src = + ../packages/vscode-ext; # yarn-source + "/libexec/vscode-squiggle/deps/vscode-squiggle"; + buildPhase = "prettier --check ."; + installPhase = "mkdir -p $out"; + }; +} diff --git a/nix/squiggle-website.nix b/nix/squiggle-website.nix new file mode 100644 index 00000000..3209affd --- /dev/null +++ b/nix/squiggle-website.nix @@ -0,0 +1,30 @@ +{ pkgs, commonFn, langFn, componentsFn }: + +rec { + common = commonFn pkgs; + lang = langFn pkgs; + components = componentsFn pkgs; + websitePackageJson = let + raw = pkgs.lib.importJSON ../packages/website/package.json; + modified = pkgs.lib.recursiveUpdate raw { + dependencies.postcss-import = "^14.1.0"; + dependencies.tailwindcss = "^3.1.8"; + }; + packageJsonString = builtins.toJSON modified; + in pkgs.writeText "packages/website/patched-package.json" packageJsonString; + yarn-source = pkgs.mkYarnPackage { + name = "squiggle-website_yarnsource"; + src = ../packages/website; + packageJSON = websitePackageJson; + yarnLock = ../yarn.lock; + packageResolutions."@quri/squiggle-lang" = lang.build; + packageResolutions."@quri/squiggle-components" = components.build; + }; + lint = pkgs.stdenv.mkDerivation { + name = "squiggle-website-lint"; + buildInputs = common.buildInputs ++ common.prettier; + src = ../packages/website; + buildPhase = "yarn lint"; + installPhase = "mkdir -p $out"; + }; +} diff --git a/nixos.sh b/nixos.sh index 91aa754f..28ff5d91 100755 --- a/nixos.sh +++ b/nixos.sh @@ -5,14 +5,14 @@ # We need to patchelf rescript executables. https://github.com/NixOS/nixpkgs/issues/107375 set -x -fhsShellName="squiggle-development" -fhsShellDotNix="{pkgs ? import {} }: (pkgs.buildFHSUserEnv { name = \"${fhsShellName}\"; targetPkgs = pkgs: [pkgs.yarn]; runScript = \"yarn\"; }).env" +fhsShellName="squiggle-fhs-development" +fhsShellDotNix="{pkgs ? import {} }: (pkgs.buildFHSUserEnv { name = \"${fhsShellName}\"; targetPkgs = pkgs: [pkgs.yarn pkgs.glibc]; runScript = \"yarn\"; }).env" nix-shell - <<<"$fhsShellDotNix" theLd=$(patchelf --print-interpreter $(which mkdir)) patchelf --set-interpreter $theLd ./node_modules/gentype/gentype.exe patchelf --set-interpreter $theLd ./node_modules/rescript/linux/*.exe patchelf --set-interpreter $theLd ./node_modules/bisect_ppx/ppx -patchelf --set-interpreter $theLd ./node_moduels/bisect_ppx/bisect-ppx-report -theSo=$(find /nix/store/*$fhsShellName*/lib64 -name libstdc++.so.6 | grep $fhsShellName | head -n 1) +patchelf --set-interpreter $theLd ./node_modules/bisect_ppx/bisect-ppx-report +theSo=$(find /nix/store/*$fhsShellName*/lib64 -name libstdc++.so.6 | head -n 1) patchelf --replace-needed libstdc++.so.6 $theSo ./node_modules/rescript/linux/ninja.exe diff --git a/packages/components/package.json b/packages/components/package.json index a82a3718..f7cc72d4 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,26 +1,26 @@ { "name": "@quri/squiggle-components", - "version": "0.3.1", + "version": "0.4.0-alpha.1", "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^1.0.0", - "@floating-ui/react-dom-interactions": "^0.9.2", + "@floating-ui/react-dom-interactions": "^0.9.3", "@headlessui/react": "^1.6.6", "@heroicons/react": "^1.0.6", "@hookform/resolvers": "^2.9.7", - "@quri/squiggle-lang": "^0.3.0", + "@quri/squiggle-lang": "^0.4.0-alpha.0", "@react-hook/size": "^2.1.2", "clsx": "^1.2.1", - "framer-motion": "^7.1.1", + "framer-motion": "^7.2.1", "lodash": "^4.17.21", "react": "^18.1.0", "react-ace": "^10.1.0", - "react-hook-form": "^7.34.1", + "react-hook-form": "^7.34.2", "react-use": "^17.4.0", "react-vega": "^7.6.0", "vega": "^5.22.1", "vega-embed": "^6.21.0", - "vega-lite": "^5.4.0", + "vega-lite": "^5.5.0", "vscode-uri": "^3.0.3", "yup": "^0.32.11" }, @@ -35,12 +35,12 @@ "@storybook/preset-create-react-app": "^4.1.2", "@storybook/react": "^6.5.10", "@testing-library/jest-dom": "^5.16.5", - "@testing-library/react": "^13.3.0", + "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^14.4.3", "@types/jest": "^27.5.0", - "@types/lodash": "^4.14.182", - "@types/node": "^18.7.4", - "@types/react": "^18.0.9", + "@types/lodash": "^4.14.184", + "@types/node": "^18.7.15", + "@types/react": "^18.0.18", "@types/styled-components": "^5.1.26", "@types/webpack": "^5.28.0", "cross-env": "^7.0.3", @@ -54,8 +54,8 @@ "tailwindcss": "^3.1.8", "ts-loader": "^9.3.0", "tsconfig-paths-webpack-plugin": "^4.0.0", - "typescript": "^4.7.4", - "web-vitals": "^2.1.4", + "typescript": "^4.8.2", + "web-vitals": "^3.0.1", "webpack": "^5.74.0", "webpack-cli": "^4.10.0", "webpack-dev-server": "^4.10.0" @@ -66,7 +66,7 @@ }, "scripts": { "start": "cross-env REACT_APP_FAST_REFRESH=false && start-storybook -p 6006 -s public", - "build:cjs": "tsc -b", + "build:cjs": "rm -rf dist/src && tsc -b", "build:css": "postcss ./src/styles/main.css -o ./dist/main.css", "build:storybook": "build-storybook -s public", "build": "yarn run build:cjs && yarn run build:css && yarn run build:storybook", @@ -74,7 +74,7 @@ "all": "yarn bundle && yarn build", "lint": "prettier --check .", "format": "prettier --write .", - "prepack": "yarn bundle && tsc -b" + "prepack": "yarn run build:cjs && yarn run bundle" }, "eslintConfig": { "extends": [ diff --git a/packages/components/src/components/DistributionChart.tsx b/packages/components/src/components/DistributionChart.tsx index d423b197..90cb4f1e 100644 --- a/packages/components/src/components/DistributionChart.tsx +++ b/packages/components/src/components/DistributionChart.tsx @@ -1,11 +1,12 @@ import * as React from "react"; import { - Distribution, + SqDistribution, result, - distributionError, - distributionErrorToString, - squiggleExpression, + SqDistributionError, resultMap, + SqRecord, + environment, + SqDistributionTag, } from "@quri/squiggle-lang"; import { Vega } from "react-vega"; import { ErrorAlert } from "./Alert"; @@ -19,7 +20,6 @@ import { NumberShower } from "./NumberShower"; import { Plot, parsePlot } from "../lib/plotParser"; import { flattenResult } from "../lib/utility"; import { hasMassBelowZero } from "../lib/distributionUtils"; -import { point } from "@quri/squiggle-lang/src/js/distribution"; export type DistributionPlottingSettings = { /** Whether to show a summary of means, stdev, percentiles etc */ @@ -29,19 +29,18 @@ export type DistributionPlottingSettings = { export type DistributionChartProps = { plot: Plot; + environment: environment; width?: number; height: number; sample?: boolean; xAxisType?: "number" | "dateTime"; } & DistributionPlottingSettings; -export function defaultPlot(distribution: Distribution): Plot { +export function defaultPlot(distribution: SqDistribution): Plot { return { distributions: [{ name: "default", distribution }] }; } -export function makePlot(record: { - [key: string]: squiggleExpression; -}): Plot | void { +export function makePlot(record: SqRecord): Plot | void { const plotResult = parsePlot(record); if (plotResult.tag === "Ok") { return plotResult.value; @@ -51,23 +50,21 @@ export function makePlot(record: { export const DistributionChart: React.FC = (props) => { const { plot, + environment, height, showSummary, width, logX, actions = false, - xAxisType = "number", } = props; const [sized] = useSize((size) => { const shapes = flattenResult( plot.distributions.map((x) => - resultMap(x.distribution.pointSet(), (shape) => ({ + resultMap(x.distribution.pointSet(environment), (pointSet) => ({ name: x.name, // color: x.color, // not supported yet - continuous: shape.continuous, - discrete: shape.discrete, + ...pointSet.asShape(), samples: [] as number[], - // samples: [] as point[], })) ) ); @@ -75,25 +72,24 @@ export const DistributionChart: React.FC = (props) => { if (shapes.tag === "Error") { return ( - {distributionErrorToString(shapes.value)} + {shapes.value.toString()} ); } // if this is a sample set, include the samples const sampleSets = plot?.distributions.filter( - (dist) => dist.distribution.t.tag === "SampleSet" + (dist) => dist.distribution.tag === SqDistributionTag.SampleSet ); if (sampleSets.length) { - for (const set of sampleSets) { - if (set.distribution.t.tag === "SampleSet") { + for (const { distribution } of sampleSets) { + if (distribution.tag === SqDistributionTag.SampleSet) { // this conditional must be duplicated to please typescript, more elegant solution probably exists - shapes.value[0].samples.push(...set.distribution.t.value); + shapes.value[0].samples.push(...distribution.value()); } } } - console.log(shapes.value); const spec = buildVegaSpec(props); let widthProp = width ? width : size.width; @@ -124,7 +120,10 @@ export const DistributionChart: React.FC = (props) => { )}
{showSummary && plot.distributions.length === 1 && ( - + )}
@@ -148,32 +147,36 @@ const Cell: React.FC<{ children: React.ReactNode }> = ({ children }) => ( ); type SummaryTableProps = { - distribution: Distribution; + distribution: SqDistribution; + environment: environment; }; -const SummaryTable: React.FC = ({ distribution }) => { - const mean = distribution.mean(); - const stdev = distribution.stdev(); - const p5 = distribution.inv(0.05); - const p10 = distribution.inv(0.1); - const p25 = distribution.inv(0.25); - const p50 = distribution.inv(0.5); - const p75 = distribution.inv(0.75); - const p90 = distribution.inv(0.9); - const p95 = distribution.inv(0.95); +const SummaryTable: React.FC = ({ + distribution, + environment, +}) => { + const mean = distribution.mean(environment); + const stdev = distribution.stdev(environment); + const p5 = distribution.inv(environment, 0.05); + const p10 = distribution.inv(environment, 0.1); + const p25 = distribution.inv(environment, 0.25); + const p50 = distribution.inv(environment, 0.5); + const p75 = distribution.inv(environment, 0.75); + const p90 = distribution.inv(environment, 0.9); + const p95 = distribution.inv(environment, 0.95); - const hasResult = (x: result): boolean => + const hasResult = (x: result): boolean => x.tag === "Ok"; const unwrapResult = ( - x: result + x: result ): React.ReactNode => { if (x.tag === "Ok") { return ; } else { return ( - {distributionErrorToString(x.value)} + {x.value.toString()} ); } diff --git a/packages/components/src/components/FunctionChart.tsx b/packages/components/src/components/FunctionChart.tsx index 73378cd8..2da5d367 100644 --- a/packages/components/src/components/FunctionChart.tsx +++ b/packages/components/src/components/FunctionChart.tsx @@ -1,10 +1,5 @@ import * as React from "react"; -import { - lambdaValue, - environment, - runForeign, - errorValueToString, -} from "@quri/squiggle-lang"; +import { SqLambda, environment, SqValueTag } from "@quri/squiggle-lang"; import { FunctionChart1Dist } from "./FunctionChart1Dist"; import { FunctionChart1Number } from "./FunctionChart1Number"; import { DistributionPlottingSettings } from "./DistributionChart"; @@ -17,7 +12,7 @@ export type FunctionChartSettings = { }; interface FunctionChartProps { - fn: lambdaValue; + fn: SqLambda; chartSettings: FunctionChartSettings; distributionPlotSettings: DistributionPlottingSettings; environment: environment; @@ -38,8 +33,8 @@ export const FunctionChart: React.FC = ({ ); } - const result1 = runForeign(fn, [chartSettings.start], environment); - const result2 = runForeign(fn, [chartSettings.stop], environment); + const result1 = fn.call([chartSettings.start]); + const result2 = fn.call([chartSettings.stop]); const getValidResult = () => { if (result1.tag === "Ok") { return result1; @@ -53,14 +48,12 @@ export const FunctionChart: React.FC = ({ if (validResult.tag === "Error") { return ( - - {errorValueToString(validResult.value)} - + {validResult.value.toString()} ); } switch (validResult.value.tag) { - case "distribution": + case SqValueTag.Distribution: return ( = ({ distributionPlotSettings={distributionPlotSettings} /> ); - case "number": + case SqValueTag.Number: return ( ; -type point = { x: number; value: result }; +type point = { x: number; value: result }; -let getPercentiles = ({ chartSettings, fn, environment }) => { +let getPercentiles = ({ + chartSettings, + fn, + environment, +}: { + chartSettings: FunctionChartSettings; + fn: SqLambda; + environment: environment; +}) => { let chartPointsToRender = _rangeByCount( chartSettings.start, chartSettings.stop, @@ -87,9 +94,9 @@ let getPercentiles = ({ chartSettings, fn, environment }) => { ); let chartPointsData: point[] = chartPointsToRender.map((x) => { - let result = runForeign(fn, [x], environment); + let result = fn.call([x]); if (result.tag === "Ok") { - if (result.value.tag === "distribution") { + if (result.value.tag === SqValueTag.Distribution) { return { x, value: { tag: "Ok", value: result.value.value } }; } else { return { @@ -104,13 +111,13 @@ let getPercentiles = ({ chartSettings, fn, environment }) => { } else { return { x, - value: { tag: "Error", value: errorValueToString(result.value) }, + value: { tag: "Error", value: result.value.toString() }, }; } }); let initialPartition: [ - { x: number; value: Distribution }[], + { x: number; value: SqDistribution }[], { x: number; value: string }[] ] = [[], []]; @@ -126,26 +133,23 @@ let getPercentiles = ({ chartSettings, fn, environment }) => { let groupedErrors: errors = _.groupBy(errors, (x) => x.value); let percentiles: percentiles = functionImage.map(({ x, value }) => { - // We convert it to to a pointSet distribution first, so that in case its a sample set - // distribution, it doesn't internally convert it to a pointSet distribution for every - // single inv() call. - let toPointSet: Distribution = unwrap(value.toPointSet()); - return { + const res = { x: x, - p1: unwrap(toPointSet.inv(0.01)), - p5: unwrap(toPointSet.inv(0.05)), - p10: unwrap(toPointSet.inv(0.1)), - p20: unwrap(toPointSet.inv(0.2)), - p30: unwrap(toPointSet.inv(0.3)), - p40: unwrap(toPointSet.inv(0.4)), - p50: unwrap(toPointSet.inv(0.5)), - p60: unwrap(toPointSet.inv(0.6)), - p70: unwrap(toPointSet.inv(0.7)), - p80: unwrap(toPointSet.inv(0.8)), - p90: unwrap(toPointSet.inv(0.9)), - p95: unwrap(toPointSet.inv(0.95)), - p99: unwrap(toPointSet.inv(0.99)), + p1: unwrap(value.inv(environment, 0.01)), + p5: unwrap(value.inv(environment, 0.05)), + p10: unwrap(value.inv(environment, 0.1)), + p20: unwrap(value.inv(environment, 0.2)), + p30: unwrap(value.inv(environment, 0.3)), + p40: unwrap(value.inv(environment, 0.4)), + p50: unwrap(value.inv(environment, 0.5)), + p60: unwrap(value.inv(environment, 0.6)), + p70: unwrap(value.inv(environment, 0.7)), + p80: unwrap(value.inv(environment, 0.8)), + p90: unwrap(value.inv(environment, 0.9)), + p95: unwrap(value.inv(environment, 0.95)), + p99: unwrap(value.inv(environment, 0.99)), }; + return res; }); return { percentiles, errors: groupedErrors }; @@ -168,19 +172,20 @@ export const FunctionChart1Dist: React.FC = ({ const signalListeners = { mousemove: handleHover, mouseout: handleOut }; //TODO: This custom error handling is a bit hacky and should be improved. - let mouseItem: result = !!mouseOverlay - ? runForeign(fn, [mouseOverlay], environment) + let mouseItem: result = !!mouseOverlay + ? fn.call([mouseOverlay]) : { tag: "Error", - value: { - tag: "RETodo", - value: "Hover x-coordinate returned NaN. Expected a number.", - }, + value: SqError.createOtherError( + "Hover x-coordinate returned NaN. Expected a number." + ), }; let showChart = - mouseItem.tag === "Ok" && mouseItem.value.tag === "distribution" ? ( + mouseItem.tag === "Ok" && + mouseItem.value.tag === SqValueTag.Distribution ? ( }; -let getFunctionImage = ({ chartSettings, fn, environment }) => { +let getFunctionImage = ({ + chartSettings, + fn, + environment, +}: { + chartSettings: FunctionChartSettings; + fn: SqLambda; + environment: environment; +}) => { let chartPointsToRender = _rangeByCount( chartSettings.start, chartSettings.stop, @@ -46,9 +49,9 @@ let getFunctionImage = ({ chartSettings, fn, environment }) => { ); let chartPointsData: point[] = chartPointsToRender.map((x) => { - let result = runForeign(fn, [x], environment); + let result = fn.call([x]); if (result.tag === "Ok") { - if (result.value.tag == "number") { + if (result.value.tag === SqValueTag.Number) { return { x, value: { tag: "Ok", value: result.value.value } }; } else { return { @@ -62,7 +65,7 @@ let getFunctionImage = ({ chartSettings, fn, environment }) => { } else { return { x, - value: { tag: "Error", value: errorValueToString(result.value) }, + value: { tag: "Error", value: result.value.toString() }, }; } }); diff --git a/packages/components/src/components/SquiggleChart.tsx b/packages/components/src/components/SquiggleChart.tsx index e68ba427..9f050b35 100644 --- a/packages/components/src/components/SquiggleChart.tsx +++ b/packages/components/src/components/SquiggleChart.tsx @@ -1,15 +1,14 @@ import * as React from "react"; import { - squiggleExpression, - bindings, + SqValue, environment, - jsImports, - defaultImports, - defaultBindings, defaultEnvironment, + resultMap, + SqValueTag, } from "@quri/squiggle-lang"; import { useSquiggle } from "../lib/hooks"; import { SquiggleViewer } from "./SquiggleViewer"; +import { JsImports } from "../lib/jsImports"; export interface SquiggleChartProps { /** The input string for squiggle */ @@ -27,14 +26,12 @@ export interface SquiggleChartProps { /** If the result is a function, the amount of stops sampled */ diagramCount?: number; /** When the squiggle code gets reevaluated */ - onChange?(expr: squiggleExpression | undefined): void; + onChange?(expr: SqValue | undefined): void; /** CSS width of the element */ width?: number; height?: number; - /** Bindings of previous variables declared */ - bindings?: bindings; /** JS imported parameters */ - jsImports?: jsImports; + jsImports?: JsImports; /** Whether to show a summary of the distribution */ showSummary?: boolean; /** Set the x scale to be logarithmic by deault */ @@ -59,6 +56,7 @@ export interface SquiggleChartProps { } const defaultOnChange = () => {}; +const defaultImports: JsImports = {}; export const SquiggleChart: React.FC = React.memo( ({ @@ -67,7 +65,6 @@ export const SquiggleChart: React.FC = React.memo( environment, onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here height = 200, - bindings = defaultBindings, jsImports = defaultImports, showSummary = false, width, @@ -85,9 +82,8 @@ export const SquiggleChart: React.FC = React.memo( distributionChartActions, enableLocalSettings = false, }) => { - const result = useSquiggle({ + const { result, bindings } = useSquiggle({ code, - bindings, environment, jsImports, onChange, @@ -113,9 +109,13 @@ export const SquiggleChart: React.FC = React.memo( count: diagramCount, }; + const resultToRender = resultMap(result, (value) => + value.tag === SqValueTag.Void ? bindings.asValue() : value + ); + return ( = (props) => { ); }; - -export interface SquigglePartialProps { - /** The text inside the input (controlled) */ - code?: string; - /** The default text inside the input (unControlled) */ - defaultCode?: string; - /** when the environment changes. Used again for notebook magic*/ - onChange?(expr: bindings | undefined): void; - /** When the code changes */ - onCodeChange?(code: string): void; - /** Previously declared variables */ - bindings?: bindings; - /** If the output requires monte carlo sampling, the amount of samples */ - environment?: environment; - /** Variables imported from js */ - jsImports?: jsImports; -} - -export const SquigglePartial: React.FC = ({ - code: controlledCode, - defaultCode = "", - onChange, - onCodeChange, - bindings = defaultBindings, - environment, - jsImports = defaultImports, -}: SquigglePartialProps) => { - const [code, setCode] = useMaybeControlledValue({ - value: controlledCode, - defaultValue: defaultCode, - onChange: onCodeChange, - }); - - const result = useSquigglePartial({ - code, - bindings, - environment, - jsImports, - onChange, - }); - - return ( - - - {result.tag !== "Ok" ? : null} - - ); -}; diff --git a/packages/components/src/components/SquiggleEditorWithImportedBindings.tsx b/packages/components/src/components/SquiggleEditorWithImportedBindings.tsx deleted file mode 100644 index 5dcc3241..00000000 --- a/packages/components/src/components/SquiggleEditorWithImportedBindings.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from "react"; -import { SquiggleEditor } from "./SquiggleEditor"; -import type { SquiggleEditorProps } from "./SquiggleEditor"; -import { runPartial, defaultBindings } from "@quri/squiggle-lang"; -import type { - result, - errorValue, - bindings as bindingsType, -} from "@quri/squiggle-lang"; - -function resultDefault(x: result): bindingsType { - switch (x.tag) { - case "Ok": - return x.value; - case "Error": - return defaultBindings; - } -} - -export type SquiggleEditorWithImportedBindingsProps = SquiggleEditorProps & { - bindingsImportUrl: string; -}; - -export const SquiggleEditorWithImportedBindings: React.FC< - SquiggleEditorWithImportedBindingsProps -> = (props) => { - const { bindingsImportUrl, ...editorProps } = props; - const [bindingsResult, setBindingsResult] = React.useState({ - tag: "Ok", - value: defaultBindings, - } as result); - React.useEffect(() => { - async function retrieveBindings(fileName: string) { - let contents = await fetch(fileName).then((response) => { - return response.text(); - }); - setBindingsResult( - runPartial( - contents, - editorProps.bindings, - editorProps.environment, - editorProps.jsImports - ) - ); - } - retrieveBindings(bindingsImportUrl); - }, [bindingsImportUrl]); - const deliveredBindings = resultDefault(bindingsResult); - return ( - - ); -}; diff --git a/packages/components/src/components/SquiggleErrorAlert.tsx b/packages/components/src/components/SquiggleErrorAlert.tsx index 31d7e352..49179497 100644 --- a/packages/components/src/components/SquiggleErrorAlert.tsx +++ b/packages/components/src/components/SquiggleErrorAlert.tsx @@ -1,11 +1,11 @@ -import { errorValue, errorValueToString } from "@quri/squiggle-lang"; +import { SqError } from "@quri/squiggle-lang"; import React from "react"; import { ErrorAlert } from "./Alert"; type Props = { - error: errorValue; + error: SqError; }; export const SquiggleErrorAlert: React.FC = ({ error }) => { - return {errorValueToString(error)}; + return {error.toString()}; }; diff --git a/packages/components/src/components/SquigglePlayground.tsx b/packages/components/src/components/SquigglePlayground.tsx index c3e38b1a..8039bbe0 100644 --- a/packages/components/src/components/SquigglePlayground.tsx +++ b/packages/components/src/components/SquigglePlayground.tsx @@ -24,7 +24,7 @@ import { } from "@heroicons/react/solid"; import clsx from "clsx"; -import { defaultBindings, environment } from "@quri/squiggle-lang"; +import { environment } from "@quri/squiggle-lang"; import { SquiggleChart, SquiggleChartProps } from "./SquiggleChart"; import { CodeEditor } from "./CodeEditor"; @@ -39,6 +39,7 @@ import { ViewSettings, viewSettingsSchema } from "./ViewSettings"; import { HeadedSection } from "./ui/HeadedSection"; import { defaultTickFormat } from "../lib/distributionSpecBuilder"; import { Button } from "./ui/Button"; +import { JsImports } from "../lib/jsImports"; type PlaygroundProps = SquiggleChartProps & { /** The initial squiggle string to put in the playground */ @@ -112,8 +113,8 @@ const SamplingSettings: React.FC<{ register: UseFormRegister }> = ({ ); const InputVariablesSettings: React.FC<{ - initialImports: any; // TODO - any json type - setImports: (imports: any) => void; + initialImports: JsImports; + setImports: (imports: JsImports) => void; }> = ({ initialImports, setImports }) => { const [importString, setImportString] = useState(() => JSON.stringify(initialImports) @@ -122,7 +123,7 @@ const InputVariablesSettings: React.FC<{ const onChange = (value: string) => { setImportString(value); - let imports = {} as any; + let imports = {}; try { imports = JSON.parse(value); setImportsAreValid(true); @@ -231,7 +232,7 @@ export const PlaygroundContext = React.createContext({ export const SquigglePlayground: FC = ({ defaultCode = "", height = 500, - showSummary = false, + showSummary = true, logX = false, expY = false, title, @@ -251,7 +252,7 @@ export const SquigglePlayground: FC = ({ onChange: onCodeChange, }); - const [imports, setImports] = useState({}); + const [imports, setImports] = useState({}); const { register, control } = useForm({ resolver: yupResolver(schema), @@ -309,7 +310,6 @@ export const SquigglePlayground: FC = ({ executionId={executionId} environment={env} {...vars} - bindings={defaultBindings} jsImports={imports} enableLocalSettings={true} /> diff --git a/packages/components/src/components/SquiggleViewer/ExpressionViewer.tsx b/packages/components/src/components/SquiggleViewer/ExpressionViewer.tsx index 51f8dcb4..1aa106c8 100644 --- a/packages/components/src/components/SquiggleViewer/ExpressionViewer.tsx +++ b/packages/components/src/components/SquiggleViewer/ExpressionViewer.tsx @@ -1,5 +1,5 @@ -import React from "react"; -import { squiggleExpression, declaration } from "@quri/squiggle-lang"; +import React, { useContext } from "react"; +import { SqDistributionTag, SqValue, SqValueTag } from "@quri/squiggle-lang"; import { NumberShower } from "../NumberShower"; import { DistributionChart, defaultPlot, makePlot } from "../DistributionChart"; import { FunctionChart, FunctionChartSettings } from "../FunctionChart"; @@ -8,7 +8,10 @@ import { VariableBox } from "./VariableBox"; import { ItemSettingsMenu } from "./ItemSettingsMenu"; import { hasMassBelowZero } from "../../lib/distributionUtils"; import { MergedItemSettings } from "./utils"; +import { ViewerContext } from "./ViewerContext"; +/* +// DISABLED FOR 0.4 branch, for now function getRange(x: declaration) { const first = x.args[0]; switch (first.tag) { @@ -31,15 +34,21 @@ function getChartSettings(x: declaration): FunctionChartSettings { count: 20, }; } +*/ const VariableList: React.FC<{ - path: string[]; + value: SqValue; heading: string; children: (settings: MergedItemSettings) => React.ReactNode; -}> = ({ path, heading, children }) => ( - +}> = ({ value, heading, children }) => ( + {(settings) => ( -
+
{children(settings)}
)} @@ -48,51 +57,44 @@ const VariableList: React.FC<{ export interface Props { /** The output of squiggle's run */ - expression: squiggleExpression; - /** Path to the current item, e.g. `['foo', 'bar', '3']` for `foo.bar[3]`; can be empty on the top-level item. */ - path: string[]; + value: SqValue; width?: number; } -export const ExpressionViewer: React.FC = ({ - path, - expression, - width, -}) => { - if (typeof expression !== "object") { - return ( - - {() => `Unknown expression: ${expression}`} - - ); - } - switch (expression.tag) { - case "number": +export const ExpressionViewer: React.FC = ({ value, width }) => { + const { getMergedSettings } = useContext(ViewerContext); + + switch (value.tag) { + case SqValueTag.Number: return ( - + {() => (
- +
)}
); - case "distribution": { - const distType = expression.value.type(); + case SqValueTag.Distribution: { + const distType = value.value.tag; return ( { - const shape = expression.value.pointSet(); + const shape = value.value.pointSet( + getMergedSettings(value.location).environment + ); return ( @@ -102,7 +104,8 @@ export const ExpressionViewer: React.FC = ({ {(settings) => { return ( = ({ ); } - case "string": + case SqValueTag.String: return ( - + {() => ( <> " - {expression.value} + {value.value} " )} ); - case "boolean": + case SqValueTag.Bool: return ( - - {() => expression.value.toString()} + + {() => value.value.toString()} ); - case "symbol": + case SqValueTag.Symbol: return ( - + {() => ( <> Undefined Symbol: - {expression.value} + {value.value} )} ); - case "call": + case SqValueTag.Call: return ( - - {() => expression.value} + + {() => value.value} ); - case "arraystring": + case SqValueTag.ArrayString: return ( - - {() => expression.value.map((r) => `"${r}"`).join(", ")} + + {() => value.value.map((r) => `"${r}"`).join(", ")} ); - case "date": + case SqValueTag.Date: return ( - - {() => expression.value.toDateString()} + + {() => value.value.toDateString()} ); - case "void": + case SqValueTag.Void: return ( - + {() => "Void"} ); - case "timeDuration": { + case SqValueTag.TimeDuration: { return ( - - {() => } + + {() => } ); } - case "lambda": + case SqValueTag.Lambda: return ( { return ( @@ -191,11 +194,11 @@ export const ExpressionViewer: React.FC = ({ > {(settings) => ( <> -
{`function(${expression.value.parameters.join( - "," - )})`}
+
{`function(${value.value + .parameters() + .join(",")})`}
= ({ )}
); - case "lambdaDeclaration": { + case SqValueTag.Declaration: { return ( { return ( ); }} > {(settings) => ( - +
NOT IMPLEMENTED IN 0.4 YET
+ // )}
); } - case "module": { + case SqValueTag.Module: { return ( - + {(_) => - Object.entries(expression.value) - .filter(([key, _]) => !key.match(/^(Math|System)\./)) + value.value + .entries() + .filter(([key, _]) => !key.match(/^(__result__)$/)) .map(([key, r]) => ( )) @@ -256,23 +260,26 @@ export const ExpressionViewer: React.FC = ({ ); } - case "record": - const plot = makePlot(expression.value); + case SqValueTag.Record: + const plot = makePlot(value.value); if (plot) { return ( { let disableLogX = plot.distributions.some((x) => { - let pointSet = x.distribution.pointSet(); + let pointSet = x.distribution.pointSet( + getMergedSettings(value.location).environment + ); return ( - pointSet.tag === "Ok" && hasMassBelowZero(pointSet.value) + pointSet.tag === "Ok" && + hasMassBelowZero(pointSet.value.asShape()) ); }); return ( = ({ return ( = ({ ); } else { return ( - + {(_) => - Object.entries(expression.value).map(([key, r]) => ( - - )) + value.value + .entries() + .map(([key, r]) => ( + + )) } ); } - case "array": + case SqValueTag.Array: return ( - + {(_) => - expression.value.map((r, i) => ( - - )) + value.value + .getValues() + .map((r, i) => ( + + )) } ); default: { return ( - + {() => (
No display for type: {" "} - - {expression.tag} - + {value.tag}
)}
diff --git a/packages/components/src/components/SquiggleViewer/ItemSettingsMenu.tsx b/packages/components/src/components/SquiggleViewer/ItemSettingsMenu.tsx index 49c2eacc..50f8e5ba 100644 --- a/packages/components/src/components/SquiggleViewer/ItemSettingsMenu.tsx +++ b/packages/components/src/components/SquiggleViewer/ItemSettingsMenu.tsx @@ -4,13 +4,14 @@ import { useForm } from "react-hook-form"; import { yupResolver } from "@hookform/resolvers/yup"; import { Modal } from "../ui/Modal"; import { ViewSettings, viewSettingsSchema } from "../ViewSettings"; -import { Path, pathAsString } from "./utils"; import { ViewerContext } from "./ViewerContext"; import { defaultTickFormat } from "../../lib/distributionSpecBuilder"; import { PlaygroundContext } from "../SquigglePlayground"; +import { SqValue } from "@quri/squiggle-lang"; +import { locationAsString } from "./utils"; type Props = { - path: Path; + value: SqValue; onChange: () => void; disableLogX?: boolean; withFunctionSettings: boolean; @@ -19,7 +20,7 @@ type Props = { const ItemSettingsModal: React.FC< Props & { close: () => void; resetScroll: () => void } > = ({ - path, + value, onChange, disableLogX, withFunctionSettings, @@ -29,7 +30,7 @@ const ItemSettingsModal: React.FC< const { setSettings, getSettings, getMergedSettings } = useContext(ViewerContext); - const mergedSettings = getMergedSettings(path); + const mergedSettings = getMergedSettings(value.location); const { register, watch } = useForm({ resolver: yupResolver(viewSettingsSchema), @@ -53,8 +54,8 @@ const ItemSettingsModal: React.FC< }); useEffect(() => { const subscription = watch((vars) => { - const settings = getSettings(path); // get the latest version - setSettings(path, { + const settings = getSettings(value.location); // get the latest version + setSettings(value.location, { ...settings, distributionPlotSettings: { showSummary: vars.showSummary, @@ -75,7 +76,7 @@ const ItemSettingsModal: React.FC< onChange(); }); return () => subscription.unsubscribe(); - }, [getSettings, setSettings, onChange, path, watch]); + }, [getSettings, setSettings, onChange, value.location, watch]); const { getLeftPanelElement } = useContext(PlaygroundContext); @@ -83,7 +84,7 @@ const ItemSettingsModal: React.FC< Chart settings - {path.length ? ( + {value.location.path.items.length ? ( <> {" for "} - {pathAsString(path)} + {locationAsString(value.location)} {" "} ) : ( @@ -120,7 +121,7 @@ export const ItemSettingsMenu: React.FC = (props) => { if (!enableLocalSettings) { return null; } - const settings = getSettings(props.path); + const settings = getSettings(props.value.location); const resetScroll = () => { if (!ref.current) return; @@ -139,7 +140,7 @@ export const ItemSettingsMenu: React.FC = (props) => { {settings.distributionPlotSettings || settings.chartSettings ? (