Skip to content

Commit

Permalink
add github pages
Browse files Browse the repository at this point in the history
  • Loading branch information
dolik-rce committed Aug 11, 2024
1 parent 17b26db commit bfc6cb1
Show file tree
Hide file tree
Showing 7 changed files with 452 additions and 0 deletions.
35 changes: 35 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Github pages

on:
push:
branches: [ master, github-pages ]

jobs:
build:
runs-on: ubuntu-24.04

permissions:
pages: write
id-token: write

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: recursive
- name: Install dependencies
shell: bash
run: |
set -ex
sudo apt-get update
sudo apt-get -y --no-install-recommends -o APT::Immediate-Configure=false install cmake emscripten
- name: Build github pages
run: ./emscripten.sh build site
- name: Upload
uses: actions/upload-pages-artifact@v3
with:
path: ./site
- name: Deploy
uses: actions/deploy-pages@v4
with:
token: ${{ github.token }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/build/
/_site/
15 changes: 15 additions & 0 deletions emscripten.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env bash

set -ex

BUILDDIR="${1:-build_js}"
SITEDIR="${2:-_site}"

emcmake cmake -B "$BUILDDIR" -DCMAKE_BUILD_TYPE=Release
export EMCC_CFLAGS="-sEXPORTED_RUNTIME_METHODS=callMain,FS -Wno-unused-command-line-argument"
cmake --build "$BUILDDIR" -v

mkdir -p "$SITEDIR"
cp -v "$BUILDDIR"/pegof.{js,wasm} "$SITEDIR/"
cp -v benchmark/grammars/*.peg "$SITEDIR/"
cp -v web/* "$SITEDIR/"
154 changes: 154 additions & 0 deletions web/glue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
let input = document.getElementById("input");
let output = document.getElementById("output");
let logs = document.getElementById("log");
let load = document.getElementById("load");
let runBtn = document.getElementById("btnRun");
let auto = document.getElementById("u");
let peg = null;
let loaded = false;

function compress(c){var x="charCodeAt",b,e={},f=c.split(""),d=[],a=f[0],g=256;for(b=1;b<f.length;b++)c=f[b],null!=e[a+c]?a+=c:(d.push(1<a.length?e[a]:a[x](0)),e[a+c]=g,g++,a=c);d.push(1<a.length?e[a]:a[x](0));for(b=0;b<d.length;b++)d[b]=String.fromCharCode(d[b]);return d.join("")}

function decompress(b){var a,e={},d=b.split(""),c=f=d[0],g=[c],h=o=256;for(b=1;b<d.length;b++)a=d[b].charCodeAt(0),a=h>a?d[b]:e[a]?e[a]:f+c,g.push(a),c=a.charAt(0),e[o]=f+c,o++,f=a;return g.join("")}

function base64Decode(base64) {
const binString = atob(base64);
return new TextDecoder().decode(Uint8Array.from(binString, (m) => m.codePointAt(0)));
}

function base64Encode(str) {
const binString = Array.from(new TextEncoder().encode(str), (byte) => String.fromCodePoint(byte)).join("");
return btoa(binString);
}

function serializeState() {
let state = { o: [], v: {} };
if (input.value != peg) {
state.i = input.value;
} else {
state.p = load.selectedOptions[0].id;
}
document.querySelectorAll("input").forEach(x => {
let v = (x.type == "checkbox") ? (x.checked ? 1 : 0) : x.value;
if (x.dataset.default != v) {
state.v[x.id] = v;
}
});
document.querySelectorAll("option:checked").forEach(x => {
if (x.dataset.default == undefined) {
state.o.push(x.id);
}
});
console.log("Serialized", state);
history.replaceState(null,null,"#" + base64Encode(compress(JSON.stringify(state))));
}

function deserializeState() {
let raw = location.hash.substr(1);
if (!raw) {
loadPeg();
return;
}
let state = JSON.parse(decompress(base64Decode(raw)));
console.log("Deserialized", state);

if (state.i) {
input.innerHTML = state.i;
peg = null;
} else {
document.getElementById(state.p).selected = true;
loadPeg();
}
state.o.forEach(opt => document.getElementById(opt).selected = true);
Object.entries(state.v).forEach(([k,v]) => {
let x = document.getElementById(k);
if (x.type == "checkbox") {
x.checked = v == 1;
} else {
x.value = v;
}
});
}

function launch() {
FS.writeFile("/tmp/input.peg", input.value);
output.innerHTML = "";
logs.innerHTML = "";

let options = [];
document.querySelectorAll(".opt option:checked").forEach(x => options.push(...x.value.split(" ")));
document.querySelectorAll("input[type=checkbox].opt").forEach(x => options.push(x.checked ? x.value : null));
document.querySelectorAll("input[type=number].opt").forEach(x => options.push(x.dataset.opt, x.value));
options.push("/tmp/input.peg");
options = options.filter(x => x && x.length > 0);

console.log("options:", options);
console.log("input:", input.value.substr(0, 50));

runBtn.disabled = true;
runBtn.innerText = "Running ...";
serializeState();
setTimeout(() => {
Module.callMain(options);
runBtn.disabled = false;
runBtn.innerText = "Run!";
}, 1);
}

function launchAuto() {
console.log("launchAuto:", auto.checked, loaded, input.value != "");
if (auto.checked && loaded && input.value != "") {
launch();
}
}

function loadPeg() {
console.log("Loading", load.value);
fetch(load.value)
.then(response => response.text())
.then(data => {
input.innerHTML = data;
peg = data;
console.log("Loaded", load.value, data.substr(0, 50));
launchAuto();
});
}

function init() {
deserializeState();

load.addEventListener("change", loadPeg);

document.querySelectorAll(".opt").forEach(x => x.addEventListener("change", launchAuto));
runBtn.addEventListener("click", launch);

let timer = null;
input.addEventListener("keydown", () => clearTimeout(timer));
input.addEventListener("keyup", e => {
if(e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
launch();
} else {
clearTimeout(timer);
timer = setTimeout(launchAuto, 1000);
}
});

document.addEventListener("DOMContentLoaded", function() {
});
}

var Module = {
noInitialRun: true,
onRuntimeInitialized: () => {
console.log("Runtime initialized");
loaded = true;
runBtn.disabled = false;
runBtn.innerText = "Run!";
launchAuto();
},

print: line => output.innerHTML += line + "\n",
printErr: line => logs.innerHTML += line + "\n"
};

init();
155 changes: 155 additions & 0 deletions web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Pegof online</title>
<link href="pegof.css" rel="stylesheet" />
</head>
<body>
<h1>Pegof online</h1>
<div id="controls">
<div class="container">
<h3>What is pegof?</h3>
<div>
Pegof is a command-line tool to format and optimize <a href="https://en.wikipedia.org/wiki/Parsing_expression_grammar">PEG</a> grammars.
It supports any grammar supported by <a href="https://github.com/arithy/packcc">PackCC</a> parser generator.
You can learn more about it in its <a href="https://github.com/dolik-rce/pegof">GitHub repository</a>.
</div>
<h3>How does it work?</h3>
<div>
Pegof parses peg grammar from input file and extracts it's <a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree">AST</a>.
Then, based on the command it either just prints it out in nice and consistent format or directly as AST.
It can also perform multi-pass optimization process that goes through the AST and tries to simplify
it as much as possible to reduce number of rules and terms, which results in faster code in the generated parser.
</div>
<h3>How to use this site?</h3>
<div>
You can update the grammar in the input field and also the configuration on the right.
The output and log fields will be automatically updated, unless auto-update is turned off.
Note that this is just a demo, the command-line tool provides more functionality,
that would be difficult to show in the web environment.
</div>
</div>
<div class="container">
<table>
<tr title="What output mode should be used">
<td>Output type:</td>
<td>
<select class="opt">
<option id="f" value="-f" selected data-default>PEG</option>
<option id="a" value="-a">AST</option>
</select>
</td>
</tr>
<tr title="Whether to write &quot;Generated by pegof&quot; header">
<td>Add header:</td>
<td>
<select class="opt">
<option id="h1" value="-H auto" selected data-default>Auto</option>
<option id="h2" value="-H always">Always</option>
<option id="h3" value="-H never">Never</option>
</select>
</td>
</tr>
<tr title="Switch between double and single quoted strings">
<td>Quotes:</td>
<td>
<select class="opt">
<option id="q1" value="-q double" selected data-default>double (")</option>
<option id="q2" value="-q single">single (')</option>
</select>
</td>
</tr>
<tr title="Wrap alternations with more than N sequences to separate lines">
<td>Wrap threshold:</td>
<td><input id="w" class="opt" type="number" data-opt="-w" data-default="1" value="1" min="1"></input></td>
<tr>
<td colspan=2>
<hr>
</td>
</tr>
<tr title="Verbose logging to stderr">
<td>Verbosity:</td>
<td>
<select class="opt">
<option id="v0" value="">Errors only</option>
<option id="v1" value="-v" selected data-default>Low</option>
<option id="v2" value="-v -v">More</option>
<option id="v3" value="-v -v -v">Even more</option>
<option id="v4" value="-v -v -v -v">Highest</option>
</select>
</td>
</tr>
<tr title="Output very verbose debug info, implies max verbosity">
<td>Debug:</td>
<td><input id="d" class="opt" type="checkbox" data-default="0" value="-d"></input></td>
</tr>
<tr title="Skip result validation (useful probably only for debugging purposes)">
<td>Skip validation:</td>
<td><input id="s" class="opt" type="checkbox" data-default="0" value="-S"></input><br></td>
</tr>
<tr>
<td colspan=2>
<hr>
</td>
</tr>
<tr title="Minimum inlining score needed for rule to be inlined, value in range 0..1, only applied when inlining is enabled">
<td>Inline limit:</td>
<td><input id="l" class="opt" type="number" data-opt="-l" data-default="0.2" value="0.2" min="0" max="1" step="0.1"></input></td>
</tr>
</table>
</div>
<div class="container">
<label title="List of optimizations to apply">
Optimizations:
<select multiple size=13 class="opt" id="optimizations">
<option id="x0" value="-O none" data-default>None</option>
<option id="x1" value="-O all">All</option>
<option id="x2" value="-O concat-char-classes">Character class concatenation</option>
<option id="x3" value="-O concat-strings">String concatenation</option>
<option id="x4" value="-O double-negation">Removing double negations</option>
<option id="x5" value="-O double-quantification">Removing double quantifications</option>
<option id="x6" value="-O inline">Rule inlining</option>
<option id="x7" value="-O normalize-char-class">Character class optimization</option>
<option id="x8" value="-O remove-group">Remove unnecessary groups</option>
<option id="x9" value="-O repeats">Removing unnecessary repeats</option>
<option id="x10" value="-O single-char-class">Convert single character classes to strings</option>
<option id="x11" value="-O unused-capture">Removing unused captures</option>
<option id="x12" value="-O unused-variable">Removing unused variables</option>
</select>
</label>
</div>
<div class="container">
<label title="List of optimizations to apply">
Load grammar:<br>
<select id="load">
<option id="p0" value="simple.peg" selected data-default>Simple</option>
<option id="p1" value="json.peg">JSON</option>
<option id="p2" value="calc.peg">Calc</option>
<option id="p3" value="c.peg">C</option>
</select>
</label>
<hr>
<label title="List of optimizations to apply">
Auto-update:
<input id="u" type="checkbox" data-default="1" checked></input>
</label>
<button disabled id="btnRun">Loading...</button>
</div>
</div>
<div class="main">
<h3>Input:</h3>
<textarea id="input" title="Modify the grammar as you wish. Ctrl+Enter can be used to process it immediately."></textarea>
</div>
<div class="main">
<h3>Output:</h3>
<pre id="output"></pre>
</div>
<div class="main">
<h3>Log:</h3>
<pre id="log"></pre>
</div>
<script src="glue.js"></script>
<script src="pegof.js"></script>
</body>
</html>
Loading

0 comments on commit bfc6cb1

Please sign in to comment.