-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
460 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 python3 ca-certificates | ||
- 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 }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
/build/ | ||
/_site/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
#!/usr/bin/env bash | ||
|
||
set -ex | ||
|
||
BUILDDIR="${1:-build_js}" | ||
SITEDIR="${2:-_site}" | ||
|
||
if [ ! -d "$BUILDDIR/emsdk" ]; then | ||
mkdir -p "$BUILDDIR" | ||
git clone https://github.com/emscripten-core/emsdk.git "$BUILDDIR/emsdk" | ||
"$BUILDDIR"/emsdk/emsdk install latest | ||
"$BUILDDIR"/emsdk/emsdk activate latest | ||
fi | ||
. "$BUILDDIR"/emsdk/emsdk_env.sh | ||
|
||
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/" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 "Generated by pegof" 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> |
Oops, something went wrong.