Skip to content

Commit

Permalink
Start work on jaq playground.
Browse files Browse the repository at this point in the history
  • Loading branch information
01mf02 committed Mar 15, 2024
1 parent e39da13 commit 6c267c6
Show file tree
Hide file tree
Showing 10 changed files with 446 additions and 0 deletions.
107 changes: 107 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ members = [
"jaq-core",
"jaq-std",
"jaq",
"jaq-play",
]

resolver = "2"

[profile.release]
Expand Down
1 change: 1 addition & 0 deletions jaq-play/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pkg
28 changes: 28 additions & 0 deletions jaq-play/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "jaq-play"
description = "Web interface for jaq"
version = "0.1.0"
authors = ["Michael Färber <[email protected]>"]
categories = ["wasm"]
readme = "README.md"
repository = "https://github.com/01mf02/jaq"
license = "MIT"
edition = "2021"

[package.metadata.wasm-pack.profile.dev]
wasm-opt = false

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
jaq-syn = { version = "1.1.0", path = "../jaq-syn" }
jaq-parse = { version = "1.0.0", path = "../jaq-parse" }
jaq-interpret = { version = "1.2.0", path = "../jaq-interpret" }
hifijson = "0.2"
log = "0.4.17"
serde_json = { version = "1.0.81", features = [ "arbitrary_precision", "preserve_order" ] }

console_log = { version = "1.0", features = ["color"] }
getrandom = { version = "0.2", features = ["js"] }
wasm-bindgen = { version = "0.2" }
14 changes: 14 additions & 0 deletions jaq-play/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
jaq playground
==============

First install `wasm-pack`:

cargo install wasm-pack

Compile the WASM binaries (use `--release` instead of `--dev` for better performance):

wasm-pack build --target web --dev

To serve:

python3 -m http.server --bind 127.0.0.1
46 changes: 46 additions & 0 deletions jaq-play/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/src/main.css">
<title>jaq playground</title>
</head>

<body>
<header class="top">
<button id="run" class="button">▶️ Run</span>
<!--
<span id="flags" class="button">⚙️ Flags</span>
-->
</header>

<section class="filter">
<header><h1>Filter</h1></header>
<textarea id="filter" spellcheck="false">.</textarea>
</section>

<div class="IO">
<section class="input">
<header><h1>Input</h1></header>
<textarea id="input" spellcheck="false">null</textarea>
</section>

<section class="output">
<header><h1>Output</h1></header>
<div id="output" spellcheck="false"></div>
</section>
</div>
<!--
<div id="footer"></div>
-->
<script type="module">
import init from "/pkg/jaq_play.js";
await init();
</script>
<script type="module" src="./src/main.js" defer></script>
</body>

</html>
18 changes: 18 additions & 0 deletions jaq-play/src/lib.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Functions that are called from the Rust code.

export function output_newline() {
const new_line = document.createElement("div");
document.getElementById("output").appendChild(new_line);
}

export function output_tag(tag, type) {
let span = document.createElement("span");
if (type) {
span.className = type;
}
span.textContent = tag;

let last_line = document.getElementById("output").lastElementChild;
last_line.appendChild(span);
}

108 changes: 108 additions & 0 deletions jaq-play/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
use wasm_bindgen::prelude::*;

#[wasm_bindgen(module = "/src/lib.js")]
extern "C" {
#[wasm_bindgen]
fn output_newline();

#[wasm_bindgen]
fn output_tag(s: &str, typ: Option<&str>);
}

use jaq_interpret::{Ctx, Error, FilterT, ParseCtx, RcIter, Val};
use serde_json::{json, Value};

fn indent(level: usize) {
if level > 0 {
output_tag(&" ".repeat(level), None);
}
}

fn print_val(v: &Val, level: usize, indent_start: bool) {
if indent_start {
indent(level);
}
match v {
Val::Null => output_tag("null", Some("null")),
Val::Bool(true) => output_tag("true", Some("bool")),
Val::Bool(false) => output_tag("false", Some("bool")),
Val::Int(i) => output_tag(&i.to_string(), Some("number")),
Val::Float(x) if x.is_finite() => output_tag(&format!("{x:?}"), Some("number")),
Val::Float(_) => output_tag("null", Some("null")),
Val::Num(n) => output_tag(n, Some("number")),
Val::Str(s) => output_tag(&format!("{s:?}"), Some("string")),
Val::Arr(a) if a.is_empty() => output_tag("[]", Some("array")),
Val::Arr(a) => {
output_tag("[", Some("array"));
output_newline();
let mut iter = a.iter().peekable();
while let Some(x) = iter.next() {
print_val(x, level + 1, true);
if iter.peek().is_some() {
output_tag(",", None);
}
output_newline();
}
indent(level);
output_tag("]", Some("array"));
}
Val::Obj(o) if o.is_empty() => output_tag("{}", Some("object")),
Val::Obj(o) => {
output_tag("{", Some("object"));
output_newline();
let mut iter = o.iter().peekable();
while let Some((k, v)) = iter.next() {
indent(level + 1);
output_tag(&format!("{k:?}"), Some("key"));
output_tag(": ", None);
print_val(v, level + 1, false);
if iter.peek().is_some() {
output_tag(",", None);
}
output_newline();
}
indent(level);
output_tag("}", Some("object"));
}
}
}

#[wasm_bindgen]
pub fn run(filter: &str, input: &str) {
let _ = console_log::init();
log::info!("Starting run in Rust ...");

let mut lexer = hifijson::SliceLexer::new(input.as_bytes());
let inputs = core::iter::from_fn(move || {
use hifijson::token::Lex;
Some(Val::parse(lexer.ws_token()?, &mut lexer).map_err(|e| todo!()))
});

// start out only from core filters,
// which do not include filters in the standard library
// such as `map`, `select` etc.
let mut defs = ParseCtx::new(Vec::new());

// parse the filter
let (f, errs) = jaq_parse::parse(filter, jaq_parse::main());
assert_eq!(errs, Vec::new());

// compile the filter in the context of the given definitions
let f = defs.compile(f.unwrap());
assert!(defs.errs.is_empty());

let inputs = RcIter::new(inputs);

for x in &inputs {
for y in f.run((Ctx::new([], &inputs), x.unwrap())) {
output_newline();
match y {
Ok(y) => print_val(&y, 0, false),
Err(e) => {
output_tag(&format!("⚠️ Error: {e}"), Some("error"));
break;
}
}
}
}
}
Loading

0 comments on commit 6c267c6

Please sign in to comment.