<!-- Interactive SSA form converter — pick a snippet and press Convert -->
<div class="toolbar">
<label for="snippet-select">Pick a program:</label>
<select id="snippet-select">
<option value="0">Simple if/else</option>
<option value="1">Two-variable branch</option>
<option value="2">Loop with accumulator</option>
</select>
<button id="convert-btn" type="button">Convert to SSA</button>
<button id="reset-btn" type="button" class="ghost">Reset</button>
</div>
<div class="panels">
<div class="panel">
<div class="panel-title">Original code</div>
<pre id="original-code"></pre>
</div>
<div class="panel">
<div class="panel-title">SSA form</div>
<pre id="ssa-code"></pre>
</div>
</div>
<div id="status" class="status"></div>
/* Layout and card styles for the SSA demo */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; padding: 14px; }
.toolbar { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; margin-bottom: .8rem; }
label { font-size: .85rem; color: #555; }
select { font: inherit; padding: .3rem .5rem; border: 1px solid #bbb; border-radius: 6px; background: #fff; }
button { font: 600 14px system-ui, sans-serif; padding: .4rem .85rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 6px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
.panels { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; }
@media (max-width: 540px) { .panels { grid-template-columns: 1fr; } }
.panel { border: 1px solid #d0d7de; border-radius: 8px; overflow: hidden; }
.panel-title { background: #f6f8fa; border-bottom: 1px solid #d0d7de;
font-size: .8rem; font-weight: 700; color: #444; padding: .35rem .7rem; }
pre { margin: 0; padding: .6rem .8rem; font: 13px/1.55 ui-monospace, monospace;
white-space: pre-wrap; min-height: 120px; background: #fff; }
/* Syntax-highlight colours */
.kw { color: #0070c1; font-weight: 700; }
.phi { color: #a020f0; font-weight: 700; }
.ver { color: #077c6e; }
.cmt { color: #888; font-style: italic; }
.num { color: #b05a00; }
.status { margin-top: .5rem; font-size: .9rem; font-weight: 600; min-height: 1.3em; }
.status.ok { color: #0a7d33; }
.status.info { color: #1d3557; }
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Three example programs as (lines, ssaLines, phiAt) triples
const SNIPPETS = [
{
// Snippet 0: simple if/else — one phi for x at the merge point
lines: [
'x = 1',
'if (cond):',
' x = 2',
'y = x + 1'
],
// SSA for snippet 0 — x3 = phi(x2, x1) merges both branches
ssaLines: [
'x₁ = 1',
'if (cond):',
' x₂ = 2',
'x₃ = φ(x₂, x₁)',
'y₁ = x₃ + 1'
],
phiAt: [3]
},
{
// Snippet 1: two-variable if/else — two phis at the merge point
lines: [
'a = 0',
'b = 1',
'if (cond):',
' a = a + b',
'else:',
' b = a - 1',
'c = a + b'
],
// SSA for snippet 1 — a3 = phi(a2,a1) and b3 = phi(b1,b2)
ssaLines: [
'a₁ = 0',
'b₁ = 1',
'if (cond):',
' a₂ = a₁ + b₁',
'else:',
' b₂ = a₁ - 1',
'a₃ = φ(a₂, a₁)',
'b₃ = φ(b₁, b₂)',
'c₁ = a₃ + b₃'
],
phiAt: [6, 7]
},
{
// Snippet 2: while loop — phis at the loop header for n and x
lines: [
'n = input()',
'x = 0',
'while (n > 0):',
' x = x + n',
' n = n - 1',
'result = x'
],
// SSA for snippet 2 — n2 = phi(n1,n3) and x2 = phi(x1,x3) at loop header
ssaLines: [
'n₁ = input()',
'x₁ = 0',
'n₂ = φ(n₁, n₃)',
'x₂ = φ(x₁, x₃)',
'while (n₂ > 0):',
' x₃ = x₂ + n₂',
' n₃ = n₂ - 1',
'result₁ = x₂'
],
phiAt: [2, 3]
}
];
// Keywords reserved in all snippets (not variable names)
const KEYWORDS = new Set(['if', 'else', 'while', 'input', 'result']);
function escHtml(s) {
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
}
// Syntax highlighter for one line of code
function highlight(line, isSSA) {
// Escape HTML first to avoid injecting tags
line = escHtml(line);
// In SSA mode highlight the phi symbol
if (isSSA) {
line = line.replace(/(φ)/g, '<span class="phi">$1</span>');
// Highlight subscripted variable versions
line = line.replace(/([a-z_]+)([₁₂₃₄₅])/g,
'<span class="ver">$1$2</span>');
}
// Highlight language keywords
line = line.replace(/\b(if|else|while|input|result)\b/g, '<span class="kw">$1</span>');
// Highlight numeric literals
line = line.replace(/\b(\d+)\b/g, '<span class="num">$1</span>');
return line;
}
function renderCode(elementId, lines, isSSA, phiAt) {
const pre = document.getElementById(elementId);
pre.innerHTML = lines.map((line, i) => {
const indent = line.match(/^\s*/)[0];
const trimmed = line.trimStart();
const isPhi = phiAt && phiAt.includes(i);
const cls = isPhi ? ' class="phi-line"' : '';
return `<span${cls}>${escHtml(indent)}${highlight(trimmed, isSSA)}</span>`;
}).join('\n');
}
let currentIdx = 0;
let converted = false;
function loadSnippet(idx) {
// Load a fresh snippet without conversion applied
currentIdx = idx;
converted = false;
const s = SNIPPETS[idx];
renderCode('original-code', s.lines, false, []);
document.getElementById('ssa-code').textContent = 'Press "Convert to SSA" to see the result.';
document.getElementById('status').textContent = '';
document.getElementById('status').className = 'status';
}
function convert() {
// Apply the stored SSA transformation for the current snippet
const s = SNIPPETS[currentIdx];
renderCode('ssa-code', s.ssaLines, true, s.phiAt);
converted = true;
const phiCount = s.phiAt.length;
document.getElementById('status').textContent =
i18nFmt('Done! %phi% φ-function(s) inserted at control-flow join(s).', { phi: phiCount });
document.getElementById('status').className = 'status ok';
}
function reset() {
// Clear the SSA panel and restore the original view
loadSnippet(currentIdx);
}
document.getElementById('snippet-select').addEventListener('change', (e) => {
loadSnippet(parseInt(e.target.value, 10));
});
document.getElementById('convert-btn').addEventListener('click', convert);
document.getElementById('reset-btn').addEventListener('click', reset);
// Render the first snippet on page load
loadSnippet(0);
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Three example programs as (lines, ssaLines, phiAt) triples
const SNIPPETS = [
{
// Snippet 0: simple if/else — one phi for x at the merge point
lines: [
'x = 1',
'if (cond):',
' x = 2',
'y = x + 1'
],
// SSA for snippet 0 — x3 = phi(x2, x1) merges both branches
ssaLines: [
'x₁ = 1',
'if (cond):',
' x₂ = 2',
'x₃ = φ(x₂, x₁)',
'y₁ = x₃ + 1'
],
phiAt: [3]
},
{
// Snippet 1: two-variable if/else — two phis at the merge point
lines: [
'a = 0',
'b = 1',
'if (cond):',
' a = a + b',
'else:',
' b = a - 1',
'c = a + b'
],
// SSA for snippet 1 — a3 = phi(a2,a1) and b3 = phi(b1,b2)
ssaLines: [
'a₁ = 0',
'b₁ = 1',
'if (cond):',
' a₂ = a₁ + b₁',
'else:',
' b₂ = a₁ - 1',
'a₃ = φ(a₂, a₁)',
'b₃ = φ(b₁, b₂)',
'c₁ = a₃ + b₃'
],
phiAt: [6, 7]
},
{
// Snippet 2: while loop — phis at the loop header for n and x
lines: [
'n = input()',
'x = 0',
'while (n > 0):',
' x = x + n',
' n = n - 1',
'result = x'
],
// SSA for snippet 2 — n2 = phi(n1,n3) and x2 = phi(x1,x3) at loop header
ssaLines: [
'n₁ = input()',
'x₁ = 0',
'n₂ = φ(n₁, n₃)',
'x₂ = φ(x₁, x₃)',
'while (n₂ > 0):',
' x₃ = x₂ + n₂',
' n₃ = n₂ - 1',
'result₁ = x₂'
],
phiAt: [2, 3]
}
];
// Keywords reserved in all snippets (not variable names)
const KEYWORDS = new Set(['if', 'else', 'while', 'input', 'result']);
function escHtml(s) {
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
}
// Syntax highlighter for one line of code
function highlight(line, isSSA) {
// Escape HTML first to avoid injecting tags
line = escHtml(line);
// In SSA mode highlight the phi symbol
if (isSSA) {
line = line.replace(/(φ)/g, '<span class="phi">$1</span>');
// Highlight subscripted variable versions
line = line.replace(/([a-z_]+)([₁₂₃₄₅])/g,
'<span class="ver">$1$2</span>');
}
// Highlight language keywords
line = line.replace(/\b(if|else|while|input|result)\b/g, '<span class="kw">$1</span>');
// Highlight numeric literals
line = line.replace(/\b(\d+)\b/g, '<span class="num">$1</span>');
return line;
}
function renderCode(elementId, lines, isSSA, phiAt) {
const pre = document.getElementById(elementId);
pre.innerHTML = lines.map((line, i) => {
const indent = line.match(/^\s*/)[0];
const trimmed = line.trimStart();
const isPhi = phiAt && phiAt.includes(i);
const cls = isPhi ? ' class="phi-line"' : '';
return `<span${cls}>${escHtml(indent)}${highlight(trimmed, isSSA)}</span>`;
}).join('\n');
}
let currentIdx = 0;
let converted = false;
function loadSnippet(idx) {
// Load a fresh snippet without conversion applied
currentIdx = idx;
converted = false;
const s = SNIPPETS[idx];
renderCode('original-code', s.lines, false, []);
document.getElementById('ssa-code').textContent = 'Press "Convert to SSA" to see the result.';
document.getElementById('status').textContent = '';
document.getElementById('status').className = 'status';
}
function convert() {
// Apply the stored SSA transformation for the current snippet
const s = SNIPPETS[currentIdx];
renderCode('ssa-code', s.ssaLines, true, s.phiAt);
converted = true;
const phiCount = s.phiAt.length;
document.getElementById('status').textContent =
i18nFmt('Done! %phi% φ-function(s) inserted at control-flow join(s).', { phi: phiCount });
document.getElementById('status').className = 'status ok';
}
function reset() {
// Clear the SSA panel and restore the original view
loadSnippet(currentIdx);
}
document.getElementById('snippet-select').addEventListener('change', (e) => {
loadSnippet(parseInt(e.target.value, 10));
});
document.getElementById('convert-btn').addEventListener('click', convert);
document.getElementById('reset-btn').addEventListener('click', reset);
// Render the first snippet on page load
loadSnippet(0);