88 lines
9.1 KiB
HTML
88 lines
9.1 KiB
HTML
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"/><meta content="width=device-width, initial-scale=1" name="viewport"/><link type="image/x-icon" rel="icon" href="resources/favicon.svg"/><!-- --><!-- --></head><body><div class="pure-g"><div class="home-menu pure-menu pure-menu-horizontal"><ul class="pure-menu-list"><li class="pure-menu-item"><a class="pure-menu-link" href="/home"> Home </a></li><li class="pure-menu-item"><a href="/projects" class="pure-menu-link"> Projects </a></li><li class="pure-menu-item"><a href="/blog" class="pure-menu-link"> Blog </a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/about"> About </a></li></ul></div><div class="pure-u-1-1"><div class="header"><h1> Rust HTML Templating Engine </h1><h2> Front End - Parser Design </h2></div><div class="content"><h2 class="distinct"> Motivation </h2>
|
||
|
HTML is a powerful tool for creating static web pages, but
|
||
|
is not easily modular or scalable. Front end frameworks like
|
||
|
React provide reusable components. I want to use components
|
||
|
without the overhead of a front-end framework. I want the final
|
||
|
output to be statically generated rather than generated on the fly
|
||
|
by the server or the client, minified and with inlined CSS and
|
||
|
JavaScript.
|
||
|
<h2 class="distinct"> Approach </h2>
|
||
|
After looking at some of the HTML parsing libraries, I was
|
||
|
unsatisfied with the approaches most developers took. The DOM
|
||
|
is represented as a tree structure, where each node keeps a
|
||
|
pointer to its children.
|
||
|
<div class="code-block">
|
||
|
<pre><code>
|
||
|
<span class="comment">// From the html_parser crate</span>
|
||
|
<span class="keyword">pub</span> <span class="keyword storage type">enum</span> <span class="type">Node</span> <span class="punctuation bracket">{</span>
|
||
|
<span class="type enum variant">Text</span><span class="punctuation bracket">(</span><span class="type">String</span><span class="punctuation bracket">)</span><span class="punctuation delimiter">,</span>
|
||
|
<span class="type enum variant">Element</span><span class="punctuation bracket">(</span><span class="type">Element</span><span class="punctuation bracket">)</span><span class="punctuation delimiter">,</span>
|
||
|
<span class="type enum variant">Comment</span><span class="punctuation bracket">(</span><span class="type">String</span><span class="punctuation bracket">)</span><span class="punctuation delimiter">,</span>
|
||
|
<span class="punctuation bracket">}</span>
|
||
|
|
||
|
<span class="keyword">pub</span> <span class="keyword storage type">struct</span> <span class="type">Element</span> <span class="punctuation bracket">{</span>
|
||
|
<span class="keyword">pub</span> <span class="variable other member">name</span>: <span class="type">String</span><span class="punctuation delimiter">,</span>
|
||
|
<span class="keyword">pub</span> <span class="variable other member">attributes</span>: <span class="type">HashMap</span><span class="punctuation bracket"><</span><span class="type">String</span><span class="punctuation delimiter">,</span> <span class="type">Option</span><span class="punctuation bracket"><</span><span class="type">String</span><span class="punctuation bracket">></span><span class="punctuation bracket">></span><span class="punctuation delimiter">,</span>
|
||
|
<span class="keyword">pub</span> <span class="variable other member">children</span>: <span class="type">Vec</span><span class="punctuation bracket"><</span><span class="type">Node</span><span class="punctuation bracket">></span><span class="punctuation delimiter">,</span>
|
||
|
<span class="comment">// other fields omitted</span>
|
||
|
<span class="punctuation bracket">}</span>
|
||
|
</code></pre>
|
||
|
</div>
|
||
|
This is bad for cache locality and effectively forces the use
|
||
|
of recursion in order to traverse the DOM. Recursion is undesirable
|
||
|
for performance and robustness reasons. My approach is to create a
|
||
|
flat array of elements.
|
||
|
<div class="code-block">
|
||
|
<pre><code>
|
||
|
<span class="comment">// My implementation (fields omitted)</span>
|
||
|
<span class="keyword">pub</span> <span class="keyword storage type">enum</span> <span class="type">HtmlElement</span> <span class="punctuation bracket">{</span>
|
||
|
<span class="type enum variant">DocType</span><span class="punctuation delimiter">,</span>
|
||
|
<span class="type enum variant">Comment</span><span class="punctuation bracket">(</span><span class="comment">/* */</span><span class="punctuation bracket">)</span><span class="punctuation delimiter">,</span>
|
||
|
<span class="type enum variant">OpenTag</span> <span class="punctuation bracket">{</span> <span class="comment">/* */</span> <span class="punctuation bracket">}</span><span class="punctuation delimiter">,</span>
|
||
|
<span class="type enum variant">CloseTag</span> <span class="punctuation bracket">{</span> <span class="comment">/* */</span> <span class="punctuation bracket">}</span><span class="punctuation delimiter">,</span>
|
||
|
<span class="type enum variant">Text</span><span class="punctuation bracket">(</span> <span class="comment">/* */</span> <span class="punctuation bracket">)</span><span class="punctuation delimiter">,</span>
|
||
|
<span class="type enum variant">Script</span> <span class="punctuation bracket">{</span> <span class="comment">/* */</span> <span class="punctuation bracket">}</span><span class="punctuation delimiter">,</span>
|
||
|
<span class="type enum variant">Style</span> <span class="punctuation bracket">{</span> <span class="comment">/* */</span> <span class="punctuation bracket">}</span><span class="punctuation delimiter">,</span>
|
||
|
<span class="type enum variant">Directive</span> <span class="punctuation bracket">{</span> <span class="comment">/* */</span> <span class="punctuation bracket">}</span><span class="punctuation delimiter">,</span>
|
||
|
<span class="punctuation bracket">}</span>
|
||
|
</code></pre>
|
||
|
</div>
|
||
|
This approach lends itself to linear iterative algorithms. An
|
||
|
element's children can be represented as a slice of the DOM array,
|
||
|
from the opening tag to its close tag.
|
||
|
<h2 class="distinct"> Templates </h2>
|
||
|
I define a "template" as a user defined element which expands into
|
||
|
a larger piece of HTML. Templates can pass on children and attributes
|
||
|
to the final output. Some special templates can perform other functions,
|
||
|
like inlining a CSS file or syntax highlighting a code block.
|
||
|
<div class="code-block">
|
||
|
<pre><code>
|
||
|
<span class="comment"><!-- Here is an example of a template definition --></span>
|
||
|
<span class="punctuation bracket"><</span><span class="tag">Echo</span><span class="punctuation bracket">></span>
|
||
|
<span class="comment"><!--
|
||
|
This means to inherit the 'class' attribute from the invocation
|
||
|
--></span>
|
||
|
<span class="punctuation bracket"><</span><span class="tag">h1</span> <span class="attribute">class</span><span class="punctuation delimiter">=</span><span class="string">@class</span><span class="punctuation bracket">></span>
|
||
|
<span class="comment"><!--
|
||
|
This means place the children of the invocation here
|
||
|
--></span>
|
||
|
<span class="punctuation bracket"><</span>@children<span class="punctuation bracket">/></span>
|
||
|
<span class="punctuation bracket"></</span><span class="tag">h1</span><span class="punctuation bracket">></span>
|
||
|
<span class="punctuation bracket"><</span><span class="tag">h2</span><span class="punctuation bracket">></span>
|
||
|
<span class="comment"><!-- This means to inherit the 'href' attribute from the invocation --></span>
|
||
|
<span class="punctuation bracket"><</span><span class="tag">a</span> <span class="attribute">href</span><span class="punctuation delimiter">=</span><span class="string">"</span><span class="string">@href</span><span class="string">"</span><span class="punctuation bracket">></span>
|
||
|
<span class="comment"><!-- Children can be duplicated any number of times --></span>
|
||
|
<span class="punctuation bracket"><</span>@children<span class="punctuation bracket">/></span>
|
||
|
<span class="punctuation bracket"></</span><span class="tag">a</span><span class="punctuation bracket">></span>
|
||
|
<span class="punctuation bracket"></</span><span class="tag">h2</span><span class="punctuation bracket">></span>
|
||
|
<span class="comment"><!--
|
||
|
Attributes can also be duplciated, and boolean attributes
|
||
|
work exactly as expected
|
||
|
--></span>
|
||
|
<span class="punctuation bracket"><</span><span class="tag">h3</span> <span class="attribute">class</span><span class="punctuation delimiter">=</span><span class="string">@class</span> <span class="attribute">hidden</span><span class="punctuation delimiter">=</span><span class="string">@hidden</span><span class="punctuation bracket">></span>
|
||
|
<span class="punctuation bracket"><</span>@children<span class="punctuation bracket">/></span>
|
||
|
<span class="punctuation bracket"></</span><span class="tag">h3</span><span class="punctuation bracket">></span>
|
||
|
<span class="punctuation bracket"></</span><span class="tag">Echo</span><span class="punctuation bracket">></span>
|
||
|
<span class="comment"><!-- End of template definition --></span>
|
||
|
</code></pre>
|
||
|
</div></div></div></div></body></html>
|