1
0
Fork
You've already forked puredit
0
A projectional editor that uses textual code as its source of truth. https://puredit.korz.dev
  • JavaScript 94.2%
  • TypeScript 3.7%
  • Svelte 1.4%
  • Jupyter Notebook 0.3%
  • Python 0.3%
2023年10月19日 18:41:57 +02:00
.github/workflows Update jupyter example 2022年03月17日 21:41:25 +01:00
.husky Configure precommit hooks 2022年02月27日 17:40:59 +01:00
.vscode Provide project-wide TypeScript and Svelte config, fix type isolation errors and replace node-sass with sass 2022年07月03日 12:45:39 +02:00
apps Update live demo 2023年10月19日 18:41:57 +02:00
packages Correct cursor position in math editor on focus 2023年09月11日 14:41:35 +02:00
.dockerignore Update Dockerfile for Jupyter example 2022年07月03日 20:10:34 +02:00
.eslintrc.cjs Skip examples for linting 2023年09月07日 17:22:25 +02:00
.gitignore Update linter settings 2022年07月02日 16:28:13 +02:00
.swcrc Setup testing environment 2022年03月04日 17:22:45 +01:00
jest.config.mjs Setup testing environment 2022年03月04日 17:22:45 +01:00
LICENSE Update README and add LICENSE 2022年07月03日 16:40:29 +02:00
package-lock.json Make Python example executable in web demo 2023年09月11日 16:49:47 +02:00
package.json Provide project-wide TypeScript and Svelte config, fix type isolation errors and replace node-sass with sass 2022年07月03日 12:45:39 +02:00
README.md Update README.md 2023年10月12日 17:17:51 +02:00
svelte.config.js Setup testing environment 2022年03月04日 17:22:45 +01:00
tsconfig.json Provide project-wide TypeScript and Svelte config, fix type isolation errors and replace node-sass with sass 2022年07月03日 12:45:39 +02:00
turbo.json Refactor build setup to use workspaces and vite 2022年02月26日 15:23:14 +01:00

Project Overview

This repository contains the accompanying source code and examples of the following paper:

Niklas Korz, Artur Andrzejak

Virtual Domain Specific Languages via Embedded Projectional Editing

Published at 22nd International Conference on Generative Programming: Concepts & Experiences (GPCE 2023), in conjunction with ACM SPLASH 2023, 22-27 October 2023, Cascais, Portugal.

Summary

We propose here an approach which represents a subset of a General-Purpose Programming Language (GPL) as GUI widgets in a hybrid editor. It relies on matching parametrized patterns against the GPL program, and displaying the matched parts as dynamically rendered widgets. Such widgets can be interpreted as components of an external DSL. Since the source code is serialized as GPL text without annotations, there is no DSL outside the editor - hence the term ‘virtual’ DSL.

The underlying GPL and the virtual DSL can be mixed in a compositional way, with zero cost of their integration. The project infrastructure does not need to be adapted. Furthermore, our approach works with mainstream GPLs like Python or JavaScript.

To lower the development effort of such virtual DSLs, we also propose an approach to generate patterns and the corresponding text-only GUI widgets from pairs of examples.

A live demo of the system can be accessed here.

Why the name Puredit?

Purism, referring to the arts, was a movement (...) where objects are represented as elementary forms devoid of detail. (Wikipedia)

The Purist editor is a projectional editor that uses textual code as its source of truth. Unlike other projectional editors, Puredit is based on the assumption that parsers are fast enough to continuously react to changes to a document and update the projections accordingly. Projections are derived from patterns on the abstract syntax tree. To make the definition of patterns easy, they are parsed from actual code snippets in the target language.

Project structure

  • apps/: executable applications and demos
    • example/: an example of a projectional data manipulation DSL based on the TypeScript programming language, which is also available at https://puredit.korz.dev/
    • generate/: an application for generating code pattern templates and projection components from samples
    • jupyter/: an example of a projectional DSL for data analysis of Excel sheets, based on Python and integrated into JupyterLab
    • parser-playground/: an experimentation playground used for debugging our pattern matching algorithm
    • python-dsl/: an example of a projectional data manipulation DSL based on the Python programming language
  • packages/: reusable packages that together are used to create a projectional editor
    • codemirror-typescript/: a client-side language server for the TypeScript programming language integrated into CodeMirror 6, based on prisma/text-editors
    • parser/: a library for defining syntax tree patterns through code templates and finding nodes matching these patterns in a program's syntax tree
    • projections/: an extension for the CodeMirror 6 editor for detecting syntax tree patterns in a document and replacing them with interactive projection widgets implemented as Svelte components
    • simple-projection/: an alternative for the Svelte-based implementation components, limited to simple linear texts widgets with string fields

Installation and development

This projects requires Node 16 LTS and npm 8. Node 18 may work but has not been tested. Then, install the dependencies using npm install.

Develop

To develop all apps and packages, run the following command:

npm run dev

This will start development servers for apps/example, apps/parser-playground and apps/python-dsl. For the JupyterLab example, see the next section.

Build

To build all apps and packages, run the following command:

npm run build

Linting and type checking

To lint and typecheck all apps and packages, run the following command:

npm run lint

TypeScript DSL example

To build and run the TypeScript DSL example, run the following commands:

npm install
npm -w apps/example run dev

Alternatively, the TypeScript DSL example can be built and run using Docker:

docker build -t puredit-example -f apps/example/Dockerfile .
docker run -p 3000:80 puredit-example

Afterwards, the example can be accessed in a browser on http://localhost:3000.

JupyterLab example

To build and run the JupyterLab DSL example, run the following commands:

Virtual environment

# Create and activate Python virtual environment
python3 -m venv .venv
source .venv/bin/activate
# Install dependencies and our Jupyter extension
npm install
pip install -r apps/jupyter/requirements.txt
npm -w apps/jupyter run build:prod
pip install apps/jupyter
# Start JupyterLab
cd apps/jupyter/example
jupyter lab

Docker

Alternatively, the JupyterLab example can be built and run using Docker:

docker build -t puredit-jupyter -f apps/jupyter/Dockerfile .
docker run -p 8888:8888 puredit-jupyter

Afterwards, JupyterLab can be accessed in a browser on http://localhost:8888.

Creating new projections

To create new projection components and their code pattern templates, you can either start from scratch or use the output of our sample-based generator as foundation.

From scratch

To create a new projection from scratch, you need to implement two things: an entrypoint for your projection that defines the pattern and the projection, as well as a Svelte component providing the implementation for your projection widget. A good starting point is to copy and adapt one of the projections from apps/example/src/projections. Use the arg, block, and contextVariable template helpers from the @puredit/parser inside your code templates to create dynamic patterns.

Using the generator

The sample-based generator takes a text files containing both code and projection samples as input. All samples are divided by an empty line. The code samples are divided from the projection samples by a line containg only --- as content. You can look at the datasets in apps/generate/examples as reference. Execute the generator as follows, replacing the argument placeholders with your according values:

npm start -w apps/generate -- \
 --language <language> \
 --parser-name <parser-name> \
 --parser-module <parser-module> \
 --projection-name <projection-name> \
 --samples <samples> \
 --target-dir <target-dir>

Note that the path of parser-module must be relative to the path of target-dir. For example, to generate the pattern and projections files removeProjection.ts and RemoveProjection.svelte for apps/generate/examples/ts-remove.txt with target app apps/examples:

npm start -w apps/generate -- \
 --language ts \
 --parser-name tsParser \
 --parser-module './parser' \
 --projection-name remove \
 --samples apps/generate/examples/ts-remove.txt \
 --target-dir apps/example/src/projections

On Windows Powershell, \ does not work for multiline commands, so write everything in one line instead:

npm start -w apps/generate -- --language ts --parser-name tsParser --parser-module "./parser" --projection-name remove --samples apps/generate/examples/ts-remove.txt --target-dir apps/example/src/projections

Similarly, to generate the pattern and projections files takeProjection.ts and TakeProjection.svelte for apps/generate/examples/py-take.txt with target app apps/jupyter:

npm start -w apps/generate -- \
 --language py \
 --parser-name pythonParser \
 --parser-module './parser' \
 --projection-name take \
 --samples apps/generate/examples/py-take.txt \
 --target-dir apps/jupyter/src/projections

Afterwards, register the new projection by adding them to the lists in apps/example/src/projections/index.ts or respectively apps/jupyter/src/projections/index.ts.

Integrating the projectional editor

To integrate the projectional editor into your own project, see apps/example, in particular apps/example/src/Editor.svelte. There, a new CodeMirror editor is created and mounted that is making use of our projectional editing extension. For a more specialized example, see apps/jupyter. In apps/jupyter/src/editor.ts, a wrapper for CodeMirror 6 is provided that makes it compatible to JupyterLab's own editor. On top of this, apps/jupyter/src/index.ts provides a Jupyter frontend extension that replaces Jupyter's own editor with our projectional editor.