Percival
Percival is a declarative data query and visualization language. It provides a reactive, web-based notebook environment for exploring complex datasets, producing interactive graphics, and sharing results.
Percival combines the flexibility of Datalog as a query language for relational data with the beauty of exploratory visualization grammars. These declarative components interact through a reactive dataflow system. Because Percival uses web technologies (including Web Workers for multithreaded, sandboxed execution), fully-interactive notebooks can be shared with anyone on the Internet, making data analyses more tangible to others.
At the core of Percival is a custom Datalog compiler, built with Rust and WebAssembly, which integrates with its notebook runtime. This compiles the query language to JavaScript through a staged evaluation process that also allows users to embed their own JavaScript code. The interface aims to be lightweight, friendly, and accessible, and there is no hidden workspace state.
This is an early-stage research project, and we welcome your feedback, so please feel free to say hello at our discussions page!
Getting Started
If you've gotten to this point in the README, please first try out the web application and demo notebook at percival.ink! The information below is technical documentation intended for contributors.
Building Percival from scratch requires Node v16+, NPM v8+, Rust 1.56+, Cargo, and Wasm-Pack installed on your machine. To build the Rust/WebAssembly portion of the project, use the command:
wasm-pack build --target web crates/percival-wasm
Next, run npm install
to install JavaScript dependencies, then run the
following command to start the development server:
npm run dev
This should open a Percival notebook in your browser, with live reloading.
Architecture
This section outlines the high-level technical design of Percival.
User Interface
Percival is a client-side web application running fully in the user's browser. The notebook interface is built with Svelte and styled with Tailwind CSS. It relies on numerous other open source libraries, including CodeMirror 6 for live code editing and syntax highlighting, Remark and KaTeX for Markdown rendering, and Vite for frontend bundling.
The code for the web frontend is located in src/
, which contains a mix of
Svelte (in src/components/
) and TypeScript (in src/lib/
). These modules are
bundled into a static website at build time, and there is no dynamic server-side
rendering.
JIT Compiler
Users write code cells in a custom dialect of Datalog, and they are translated
to JavaScript by a Rust compiler, which itself is compiled to WebAssembly using
wasm-bindgen. The Percival
compiler's code is located in the crates/
folder. For ergonomic parsing with
human-readable error messages, the compiler relies on
chumsky, a parser combinator library.
After the percival-wasm
crate is compiled to WebAssembly, it can be used by
client-side code. The compiler processes code cells, then sends the resulting
JavaScript to separate
web workers
that sandbox the code and execute it just-in-time. As the user writes queries,
their notebook automatically tracks inter-cell dependencies and evaluates cells
in topological order, spawning / terminating worker threads on demand.
Data Visualization
Plotting is done using a specialized web worker that runs JavaScript code with access to the Observable Plot library. In order for this library (and D3) to run in a worker context, we patch the global document with a lightweight virtual DOM implementation ported from Domino.
Deployment
In production, the main
branch of this repository is continuously deployed to
percival.ink via Vercel, which
hosts the static website. It also runs a serverless function (see
api/index.go
) that allows users to share notebooks through the GitHub Gist
API.
Development
To build, lint, and format the Svelte project, use the corresponding scripts:
npm run build
npm run check
npm run format
For the Rust crates, you can run unit tests for the core functionality with:
cargo test
You can also run tests for the WebAssembly component using a headless Chrome or Firefox browser:
wasm-pack test --chrome --headless crates/percival-wasm
Since Percival uses a Rust-based compiler but outputs JavaScript, the easiest way to test code generation functionality is within the browser. We use Mocha and Puppeteer for this, and tests can be run with:
npm test
Acknowledgements
Created by Eric Zhang (@ekzhang1). Licensed under the MIT license.