1587 lines
69 KiB
HTML
1587 lines
69 KiB
HTML
<!DOCTYPE html>
|
||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||
<!-- START: two lines up starts content from: inc/top.default.html -->
|
||
|
||
<!-- END: this line ends content from: inc/top.default.html -->
|
||
<!-- START: this line starts content from: inc/head.default.html -->
|
||
|
||
<head>
|
||
<link rel="stylesheet" href="../../ioccc.css">
|
||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&display=swap" rel="stylesheet">
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
||
<title>2024/maffiodo - Prize in creative interpretation</title>
|
||
<link rel="icon" type="image/x-icon" href="../../favicon.ico">
|
||
<meta name="description" content="2024 IOCCC entry maffiodo - Prize in creative interpretation">
|
||
<meta name="keywords" content="IOCCC, 2024, IOCCC 2024, IOCCC entry, maffiodo, Prize in creative interpretation">
|
||
</head>
|
||
|
||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||
<!-- !!! DO NOT MODIFY THIS FILE - This file is generated by a tool !!! -->
|
||
<!-- !!! DO NOT MODIFY THIS FILE - This file is generated by a tool !!! -->
|
||
<!-- !!! DO NOT MODIFY THIS FILE - This file is generated by a tool !!! -->
|
||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||
|
||
<!-- END: this line ends content from: inc/head.default.html -->
|
||
|
||
<!-- -->
|
||
<!-- This web page was formed via the tool: unspecified -->
|
||
<!-- The main section of this web page came from JSON and other data files -->
|
||
<!-- -->
|
||
|
||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||
<!-- The main section of this web page was generated via the tool: unspecified -->
|
||
<!-- The main section of this web page was generated via the tool: unspecified -->
|
||
<!-- The main section of this web page was generated via the tool: unspecified -->
|
||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||
|
||
<!-- Markdown content was converted into HTML via the tool: bin/md2html.sh -->
|
||
|
||
<!-- START: this line starts content from: inc/body.default.html -->
|
||
|
||
<body>
|
||
|
||
<!-- END: this line ends content from: inc/body.default.html -->
|
||
<!-- START: this line starts content from: inc/topbar.default.html -->
|
||
|
||
<div class="theader">
|
||
<nav class="topbar">
|
||
<div class="container">
|
||
<div class="logo">
|
||
<a href="../../index.html" class="logo-link">
|
||
IOCCC
|
||
</a>
|
||
</div>
|
||
|
||
<div class="topbar-items">
|
||
<div class="item">
|
||
<span class="item-header">
|
||
Entries
|
||
</span>
|
||
|
||
<div class="sub-item">
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../years.html" class="sub-item-link">
|
||
Winning entries
|
||
</a>
|
||
</div>
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../authors.html" class="sub-item-link">
|
||
Winning authors
|
||
</a>
|
||
</div>
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../location.html" class="sub-item-link">
|
||
Location of authors
|
||
</a>
|
||
</div>
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../bugs.html" class="sub-item-link">
|
||
Bugs and (mis)features
|
||
</a>
|
||
</div>
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../faq.html#fix_an_entry" class="sub-item-link">
|
||
Fixing entries
|
||
</a>
|
||
</div>
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../faq.html#fix_author" class="sub-item-link">
|
||
Updating author info
|
||
</a>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<div class="item">
|
||
<span class="item-header">
|
||
Status
|
||
</span>
|
||
|
||
<div class="sub-item">
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../news.html" class="sub-item-link">
|
||
News
|
||
</a>
|
||
</div>
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../status.html" class="sub-item-link">
|
||
Contest status
|
||
</a>
|
||
</div>
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../next/rules.html" class="sub-item-link">
|
||
IOCCC rules
|
||
</a>
|
||
</div>
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../next/guidelines.html" class="sub-item-link">
|
||
IOCCC guidelines
|
||
</a>
|
||
</div>
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../markdown.html" class="sub-item-link">
|
||
Markdown guidelines
|
||
</a>
|
||
</div>
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../SECURITY.html" class="sub-item-link">
|
||
Security policy
|
||
</a>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<div class="item">
|
||
<span class="item-header">
|
||
FAQ
|
||
</span>
|
||
|
||
<div class="sub-item">
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../faq.html" class="sub-item-link">
|
||
Frequently asked questions
|
||
</a>
|
||
</div>
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../quick-start.html#enter" class="sub-item-link">
|
||
Enter the IOCCC
|
||
</a>
|
||
</div>
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../faq.html#compiling" class="sub-item-link">
|
||
Compiling entries
|
||
</a>
|
||
</div>
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../faq.html#running_entries" class="sub-item-link">
|
||
Running entries
|
||
</a>
|
||
</div>
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../faq.html#help" class="sub-item-link">
|
||
How to help
|
||
</a>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<div class="item">
|
||
<span class="item-header">
|
||
About
|
||
</span>
|
||
|
||
<div class="sub-item">
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../index.html" class="sub-item-link">
|
||
Home page
|
||
</a>
|
||
</div>
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../social.html" class="sub-item-link">
|
||
IOCCC social media
|
||
</a>
|
||
</div>
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../about.html" class="sub-item-link">
|
||
About the IOCCC
|
||
</a>
|
||
</div>
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../judges.html" class="sub-item-link">
|
||
The judges
|
||
</a>
|
||
</div>
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../thanks-for-help.html" class="sub-item-link">
|
||
Thanks for the help
|
||
</a>
|
||
</div>
|
||
|
||
<div class="outfit-font">
|
||
<a href="../../contact.html" class="sub-item-link">
|
||
Contact us
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<div class="header-mobile-menu">
|
||
<noscript>
|
||
<a href="../../nojs-menu.html" class="topbar-js-label">
|
||
Please Enable JavaScript
|
||
</a>
|
||
</noscript>
|
||
|
||
<button id="header-open-menu-button" class="topbar-mobile-menu">
|
||
<img
|
||
src="../../png/hamburger-icon-open.png"
|
||
alt="hamburger style menu icon - open state"
|
||
width=48
|
||
height=48>
|
||
</button>
|
||
|
||
<button id="header-close-menu-button" class="hide-content">
|
||
<img
|
||
src="../../png/hamburger-icon-closed.png"
|
||
alt="hamburger style menu icon - closed state"
|
||
width=48
|
||
height=48>
|
||
</button>
|
||
|
||
<div id="mobile-menu-panel" class="hide-content">
|
||
<div class="mobile-menu-container">
|
||
|
||
<div class="mobile-menu-wrapper">
|
||
<div class="mobile-menu-item">
|
||
Entries
|
||
</div>
|
||
<div class="mobile-submenu-wrapper">
|
||
|
||
<a class="mobile-submenu-item" href="../../years.html">
|
||
Winning entries
|
||
</a>
|
||
|
||
<a class="mobile-submenu-item" href="../../authors.html">
|
||
Winning authors
|
||
</a>
|
||
|
||
<a class="mobile-submenu-item" href="../../location.html">
|
||
Location of authors
|
||
</a>
|
||
|
||
<a class="mobile-submenu-item" href="../../bugs.html">
|
||
Bugs and (mis)features
|
||
</a>
|
||
|
||
<a class="mobile-submenu-item" href="../../faq.html#fix_an_entry">
|
||
Fixing entries
|
||
</a>
|
||
|
||
<a class="mobile-submenu-item" href="../../faq.html#fix_author">
|
||
Updating author info
|
||
</a>
|
||
|
||
<a class="mobile-submenu-item" href="../../thanks-for-help.html">
|
||
Thanks for the help
|
||
</a>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mobile-menu-wrapper">
|
||
|
||
<div class="mobile-menu-item">
|
||
Status
|
||
</div>
|
||
<div class="mobile-submenu-wrapper">
|
||
|
||
<a class="mobile-submenu-item" href="../../news.html">
|
||
News
|
||
</a>
|
||
|
||
<a class="mobile-submenu-item" href="../../status.html">
|
||
Contest status
|
||
</a>
|
||
|
||
<a class="mobile-submenu-item" href="../../next/rules.html">
|
||
IOCCC rules
|
||
</a>
|
||
|
||
<a class="mobile-submenu-item" href="../../next/guidelines.html">
|
||
IOCCC guidelines
|
||
</a>
|
||
|
||
<a class="mobile-submenu-item" href="../../markdown.html">
|
||
Markdown guidelines
|
||
</a>
|
||
|
||
<a class="mobile-submenu-item" href="../../SECURITY.html">
|
||
Security policy
|
||
</a>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mobile-menu-wrapper">
|
||
<div class="mobile-menu-item">
|
||
FAQ
|
||
</div>
|
||
<div class="mobile-submenu-wrapper">
|
||
|
||
<a class="mobile-submenu-item" href="../../faq.html">
|
||
Frequently asked questions
|
||
</a>
|
||
|
||
<a class="mobile-submenu-item" href="../../quick-start.html#enter">
|
||
Enter the IOCCC
|
||
</a>
|
||
|
||
<a class="mobile-submenu-item" href="../../faq.html#compiling">
|
||
Compiling entries
|
||
</a>
|
||
|
||
<a class="mobile-submenu-item" href="../../faq.html#running_entries">
|
||
Running entries
|
||
</a>
|
||
|
||
<a class="mobile-submenu-item" href="../../faq.html#help">
|
||
How to help
|
||
</a>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mobile-menu-wrapper">
|
||
<div class="mobile-menu-item">
|
||
About
|
||
</div>
|
||
<div class="mobile-submenu-wrapper">
|
||
|
||
<a class="mobile-submenu-item" href="../../index.html">
|
||
Home page
|
||
</a>
|
||
|
||
<a class="mobile-submenu-item" href="../../social.html">
|
||
IOCCC social media
|
||
</a>
|
||
|
||
<a class="mobile-submenu-item" href="../../about.html">
|
||
About the IOCCC
|
||
</a>
|
||
|
||
<a class="mobile-submenu-item" href="../../judges.html">
|
||
The judges
|
||
</a>
|
||
|
||
<a class="mobile-submenu-item" href="../../contact.html">
|
||
Contact us
|
||
</a>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
var headerOpenMenuButton = document.getElementById("header-open-menu-button");
|
||
var headerCloseMenuButton = document.getElementById("header-close-menu-button");
|
||
var mobileMenuPanel = document.getElementById("mobile-menu-panel");
|
||
|
||
headerOpenMenuButton.addEventListener("click", () => {
|
||
headerOpenMenuButton.classList.remove("topbar-mobile-menu");
|
||
headerOpenMenuButton.classList.add("hide-content");
|
||
|
||
headerCloseMenuButton.classList.remove("hide-content");
|
||
headerCloseMenuButton.classList.add("topbar-mobile-menu");
|
||
|
||
mobileMenuPanel.classList.remove("hide-content");
|
||
mobileMenuPanel.classList.add("topbar-mobile-panel");
|
||
});
|
||
|
||
headerCloseMenuButton.addEventListener("click", () => {
|
||
headerCloseMenuButton.classList.remove("topbar-mobile-menu");
|
||
headerCloseMenuButton.classList.add("hide-content");
|
||
|
||
mobileMenuPanel.classList.add("hide-content");
|
||
mobileMenuPanel.classList.remove("topbar-mobile-panel");
|
||
|
||
headerOpenMenuButton.classList.add("topbar-mobile-menu");
|
||
headerOpenMenuButton.classList.remove("hide-content");
|
||
});
|
||
</script>
|
||
|
||
<!-- END: this line ends content from: inc/topbar.default.html -->
|
||
<!-- START: this line starts content from: inc/header.default.html -->
|
||
|
||
<div class="header">
|
||
<a href="../../2011/zucker/index.html">
|
||
<img src="../../png/ioccc.png"
|
||
alt="IOCCC image by Matt Zucker"
|
||
width=300
|
||
height=110>
|
||
</a>
|
||
<h1>The International Obfuscated C Code Contest</h1>
|
||
<h2>2024/maffiodo - Prize in creative interpretation</h2>
|
||
<h3>Jav*script interpreter</h3>
|
||
</div>
|
||
|
||
<!-- END: this line ends content from: inc/header.default.html -->
|
||
<!-- START: this line starts content from: inc/navbar.mid.html -->
|
||
|
||
<div class="navbar">
|
||
<a class="Left" href="../kurdyukov4/index.html">← 2024/kurdyukov4</a>
|
||
<a class="Left" href="../index.html">↑ 2024 ↑</a>
|
||
<a class="Left" href="../mills/index.html">2024/mills →</a>
|
||
<a class="Right" href="https://github.com/ioccc-src/winner/blob/master/2024/maffiodo/prog.c">C code</a>
|
||
<a class="Right" href="https://github.com/ioccc-src/winner/blob/master/2024/maffiodo/Makefile">Makefile</a>
|
||
<a class="Right" href="#inventory">Inventory</a>
|
||
<a class="Right" href="https://validator.w3.org/nu/?doc=#">✓</a>
|
||
</div>
|
||
|
||
<!-- END: this line ends content from: inc/navbar.mid.html -->
|
||
<!-- START: this line starts content from: inc/before-content.default.html -->
|
||
|
||
<div class="content" id="content">
|
||
|
||
<!-- END: this line ends content from: inc/before-content.default.html -->
|
||
<!-- START: this line starts content for HTML phase 20 by: bin/output-index-author.sh via bin/md2html.sh -->
|
||
|
||
<!-- START: this line starts content generated by: bin/output-index-author.sh -->
|
||
|
||
<h2 id="author">Author:</h2>
|
||
<ul>
|
||
<li>Name: <a href="../../authors.html#Sandro_Maffiodo">Sandro Maffiodo</a><br>
|
||
Location: <a href="../../location.html#IT">IT</a> - <em>Italian Republic</em> (<em>Italy</em>)</li>
|
||
</ul>
|
||
|
||
<!-- END: next line ends content generated by: bin/output-index-author.sh -->
|
||
|
||
<!-- END: this line ends content for HTML phase 20 by: bin/output-index-author.sh via bin/md2html.sh -->
|
||
<!-- START: this line starts content for HTML phase 21 by: bin/pandoc-wrapper.sh via bin/md2html.sh -->
|
||
|
||
<!-- BEFORE: 1st line of markdown file: 2024/maffiodo/README.md -->
|
||
<h2 id="to-build">To build:</h2>
|
||
<pre><code> make all</code></pre>
|
||
<h2 id="to-use">To use:</h2>
|
||
<pre><code> ./prog < script.js</code></pre>
|
||
<h2 id="try">Try:</h2>
|
||
<pre><code> ./try.sh</code></pre>
|
||
<h2 id="judges-remarks">Judges’ remarks:</h2>
|
||
<p>This is the latest entry into the IOCCC compendium of programming language tools.</p>
|
||
<p>One example is regrettably missing: a quine.
|
||
Can you write one in the implemented subset of the language?</p>
|
||
<h2 id="authors-remarks">Author’s remarks:</h2>
|
||
<h3 id="javscript">Jav*script !!</h3>
|
||
<h4 id="whats-this">What’s this?</h4>
|
||
<p>Jav*script interpreter with Object, Array, String, Number, Functions and
|
||
Garbage Collector. Batteries are not included.</p>
|
||
<h4 id="run-forrest-run">Run Forrest, run</h4>
|
||
<p>If you want to execute some tests run one of these shell commands (these are
|
||
also included inside try.sh script):</p>
|
||
<pre><code> cat test.js string.js | ./prog
|
||
cat test.js array.js | ./prog
|
||
cat test.js math.js | ./prog
|
||
cat test.js utils.js | ./prog
|
||
cat utils.js verify.js | ./prog</code></pre>
|
||
<h3 id="remarks">Remarks</h3>
|
||
<p>This interpreter has been tested on</p>
|
||
<ul>
|
||
<li>Ubuntu 24.10 64bit x86</li>
|
||
<li>MacOS Sequoia M3 CPU</li>
|
||
</ul>
|
||
<p>Disabled warnings via Makefile:</p>
|
||
<ul>
|
||
<li><strong>Wno-implicit-int</strong> to reduce the number of tokens, lower than 2503</li>
|
||
<li><strong>Wno-incompatible-pointer-types</strong> for sprintf and strlen alias function
|
||
pointers</li>
|
||
<li><strong>Wno-return-type</strong> for functions that does not return a value (99% of times
|
||
are the same functions that uses implicit int as return type but do not use
|
||
any return statement)</li>
|
||
<li><strong>Wno-deprecated-non-prototype</strong> because on clang some function pointers like
|
||
strdup(char<em>) is aliased as (</em>)() without any explicit parameter. On gcc this
|
||
is not a problem but on MacOS clang this is needed to suppress a warning</li>
|
||
<li><strong>Wno-deprecated-declarations</strong> (probably not needed) on clang sprintf is
|
||
deprecated because is always better to use snprintf. This warning need to be
|
||
disabled because snprintf requires more tokens</li>
|
||
<li><strong>Wno-parentheses</strong> for “if (j = 0)” cases where assignment is made inside
|
||
conditional expressions, to reduce the total number of tokens used</li>
|
||
<li><strong>Wno-unused-variable</strong> because some of the variables are not used but assigned</li>
|
||
<li><strong>Wno-misleading-indentation</strong> because compiler can sometimes doubt the correct
|
||
indentation; wrong indentation can sometimes be an indicator of possible
|
||
errors, not in this case!</li>
|
||
<li><strong>Wno-strict-prototypes</strong> for clang, related to implicit-int for functions
|
||
declared without a full prototype</li>
|
||
</ul>
|
||
<p>Included files via Makefile (to reduce number of tokens, lower than 5006/2):</p>
|
||
<ul>
|
||
<li>include stdio.h</li>
|
||
<li>include string.h</li>
|
||
<li>include stdlib.h</li>
|
||
<li>include ctype.h</li>
|
||
</ul>
|
||
<h3 id="features">Features</h3>
|
||
<p>Supported and unsupported features (everything else that is not listed is
|
||
probably unsupported, but who knows…):</p>
|
||
<ul>
|
||
<li>supported binary operators (in execution priority order from lower to higher);
|
||
<pre>, = == != & &= && | |= || > >= >> < <= << - -= + += * \*= / /= % %= [ ( .</pre>
|
||
this is not standard so keep in mind that the standard one can be found
|
||
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence">here</a>
|
||
and the order of execution in this interpreter is slightly different.</li>
|
||
<li>binary operator x() call functions. Ah, yes, the interpreter support both
|
||
native and interpreted functions; inside this (light) obfuscated version of
|
||
the program, only 1 native function has been included (print), to demonstrate
|
||
this feature; The interpreter support infinite native functions, as global
|
||
functions or (in theory) as Object property.</li>
|
||
<li>operator x[y] can be used to access Object properties dynamically using String
|
||
or Numbers as y index.</li>
|
||
<li>operator x.y works like x[y] but requires that y is an identifier</li>
|
||
<li>supported unary operators: ~ ! - +</li>
|
||
<li>support infinite nested Objects and Arrays</li>
|
||
<li>+String does not convert the given String to a Number</li>
|
||
<li>multiple unary operators cannot be chained without parenthesis</li>
|
||
<li>Object properties lookup is implemented with O(N) complexity, using a linked
|
||
list instead of a dictionary or map</li>
|
||
<li>UNICODE strings are not supported. Use ASCII like if we were in 1963</li>
|
||
<li>Function can be used as anonymous callback inside function calls (for example
|
||
in Array.filter, see <strong>Useful things</strong> chapter)</li>
|
||
<li>literal String can include a limited set of escape codes: \n \” \’
|
||
or escape code with hexadecimal notation \x00..\xFF. Please note that only
|
||
ASCII characters are supported so escape codes cannot exceed two hexadecimal
|
||
characters. For example this string:</li>
|
||
</ul>
|
||
<pre><code> "abc\\xffabc"</code></pre>
|
||
<p>need to be break into two substrings to prevent “ffabc” to be parsed as a
|
||
as a single character escape code:</p>
|
||
<pre><code> "abc\\xff" + "abc"</code></pre>
|
||
<ul>
|
||
<li>postfix and prefix ++ – are not supported. Use</li>
|
||
</ul>
|
||
<pre><code> x += 1; x -= 1;</code></pre>
|
||
<ul>
|
||
<li>only <strong>if else while return</strong> and <strong>break</strong> statement control operations are
|
||
supported. Use <strong>while</strong> instead of <strong>for</strong></li>
|
||
<li>Number are implemented using <strong>double</strong> as internal data type. To save some
|
||
RAM, is possible to change this to use <strong>float</strong> instead but the code need
|
||
to be changed manually (there is no define or typedef for this data type).</li>
|
||
<li>operators with 3 or more characters length (like === or !== or >>=) are not
|
||
implemented</li>
|
||
<li>support infinite nested <em>small</em> statements (without {}) and <em>‘fat’</em> statements
|
||
(with {})</li>
|
||
<li>String concatenation with + and += is supported. Concatenated string does not
|
||
has a maximum length limit but literal strings are limited to 99 characters
|
||
(you can change this limit inside the code)</li>
|
||
<li>comparison of Object Number and String is implemented similar to the ECMA 1
|
||
specification but not fully implemented. See <strong>Equality</strong> chapter</li>
|
||
<li>does not support full Object or Array literal declaration, only the shortest
|
||
one with {} for Object and [] for Array</li>
|
||
<li>does not support Object Prototype. Object Array Number Function and String
|
||
does not have any default methods. Only “length” property is supported and is
|
||
reserved for internal use</li>
|
||
<li><strong>var</strong> is supported but <strong>let</strong> is not</li>
|
||
<li><strong>var</strong> works well only with one variable at a time. If you declare multiple
|
||
variables on the same <em>var</em> line (using comma) only the first one will be
|
||
declared correctly in local scope, overriding any existing previous variable
|
||
with the same ID. If multiple variables are declared on the same <em>var</em> line,
|
||
the next variables (after the first one) will be linked with existing
|
||
variables that has the same ID.</li>
|
||
<li><strong>var</strong> is also optional but, when is not used, all undeclared variables will
|
||
be declared automatically in local scope (and collected after current
|
||
statement ends)</li>
|
||
<li><strong>only</strong> single line comments are supported</li>
|
||
<li>multi line comments are not supported. Use only single line comments</li>
|
||
<li>any unary operator on String value will convert String to Number</li>
|
||
<li>NaN is not implemented so invalid number conversion will generate a 0 Number</li>
|
||
<li>division by zero is not handled</li>
|
||
<li>there is no error handling, so if something goes wrong, interpreter will
|
||
crash. If some of the unsupported language features are used, parser will
|
||
continue to parse what it can understand (wrongly)</li>
|
||
<li>expressions must <em>always</em> terminate with a semicolon; otherwise, unexpected
|
||
behavior will definitely happen inside the parser</li>
|
||
<li><strong>length</strong> property is a reserved property name so cannot be declared in any
|
||
Object</li>
|
||
<li>identifier does not include $ character, only alphanumeric characters and
|
||
the underscore character. Identifier maximum length is 99 character but you
|
||
can increment that by changing one constant in the beginning of the file</li>
|
||
<li><strong>delete</strong> is not implemented. You can add any property to an Object but you
|
||
cannot remove them! This can be managed by creating new Object or Array
|
||
without the property to be removed</li>
|
||
<li><strong>true</strong> and <strong>false</strong> does not exists but can be declared as variables, like
|
||
this:</li>
|
||
</ul>
|
||
<pre><code> var true = 1;
|
||
var false = 0;</code></pre>
|
||
<ul>
|
||
<li><strong>length</strong> will return the number of properties inside an Object. This is
|
||
also true for Array (see <strong>Strange(r) Things</strong> chapter)</li>
|
||
<li><strong>length</strong> on String variables (or literal) return the number of characters
|
||
in the string</li>
|
||
<li>there is no slice or splice in String and Array (but these feature can be
|
||
partially implemented in plain JavaScript – see <strong>Useful things</strong> chapter)</li>
|
||
<li>there is no Date RegEx or Math (but some of Math methods can be implemented
|
||
in plain JavaScript)</li>
|
||
<li>subscript operation like</li>
|
||
</ul>
|
||
<pre><code> x[1]</code></pre>
|
||
<p>return the property 1 of the object x or returns the character at position 1
|
||
if x is a String
|
||
- subscript operation like</p>
|
||
<pre><code> x["abc"]</code></pre>
|
||
<p>always declare a new property “abc” also when the Object x does not contains
|
||
“abc”. Normally in ECMA, if a property does not exists, undefined is returned
|
||
and, if the property is assigned, a new property is created to hold a new
|
||
value. This is not true in this mini nano tiny puppy ugly interpreter
|
||
- null and undefined are not implemented but can be declared like this:</p>
|
||
<pre><code> var null = {};
|
||
var undefined = {};</code></pre>
|
||
<p>but something different will happen when you perform some comparisons:</p>
|
||
<ul>
|
||
<li><p>null == 0 will be equal to 1</p></li>
|
||
<li><p>undefined == 0 will be equal to 1</p></li>
|
||
<li><p>undefined == null will be equal to 0</p></li>
|
||
<li><p>null == {} will be false (correctly)</p></li>
|
||
<li><p>… some other edge cases, can you spot them?</p></li>
|
||
<li><p>there is no <code>typeof</code> nor <code>instanceof</code>. You need to know the types of your
|
||
variables or track its type inside other data structures.</p></li>
|
||
<li><p><code>this</code> is missing. Inside Function you need to remember <code>this</code>.</p></li>
|
||
<li><p>Function declaration is supported ONLY inside expression and not as a
|
||
standalone declarator, so instead of this:</p></li>
|
||
</ul>
|
||
<pre><code> function x() {}</code></pre>
|
||
<p>write this:</p>
|
||
<pre><code> var x = function () {};</code></pre>
|
||
<p>and please look at the final semicolon! It is very important because every
|
||
expression need to terminate with that and without that strange parsing
|
||
things will happen (also probably some segmentation fault).
|
||
Functions need to be unnamed by itself.
|
||
Functions can be used as callback inside other function calls but read the
|
||
following limitation…
|
||
- there is no closure mechanism so, if your function work on some variables
|
||
outside of the function scope, be warned, something strange will happen. If
|
||
possible use a <em>pure functional</em> approach.
|
||
- <strong>function</strong> named parameters are not supported, for example:</p>
|
||
<pre><code> function (a, b, c) { return a+b; }</code></pre>
|
||
<p>need to be rewritten as:</p>
|
||
<pre><code> function () { return arguments[0] + arguments[1]; }</code></pre>
|
||
<p>using <strong>arguments</strong> array. Another solution is to map arguments to new local
|
||
variables using <strong>var</strong>, for example:</p>
|
||
<pre><code> function () {
|
||
var a = arguments[0];
|
||
var b = arguments[1];
|
||
return a+b;
|
||
}</code></pre>
|
||
<ul>
|
||
<li><strong>arguments</strong> array cannot be used directly for nested function calls, so
|
||
instead of this:</li>
|
||
</ul>
|
||
<pre><code> var foo = function () {
|
||
print(arguments[0] + ' ' + arguments[1]);
|
||
};</code></pre>
|
||
<p>you need to write that:</p>
|
||
<pre><code> var foo = function () {
|
||
var string = arguments[0] + ' ' + arguments[1];
|
||
print(string); //<-- here, inside parenthesis, arguments cannot be used and
|
||
// will cause a missmatch between the current arguments and
|
||
// the new one allocated for print Function
|
||
};</code></pre>
|
||
<p>because internally <strong>arguments</strong> array will be re-declared locally, with a
|
||
stack based approach, for the next function call and can cause some variable
|
||
resolving mismatch. This is a little annoying but is also better to always
|
||
remap each arguments value to a dedicated local variable.
|
||
- early exit inside logical expression is not implemented so this code will not
|
||
be interrupted before the execution of the <code>foo2</code> function:</p>
|
||
<pre><code> if (0 && foo2(123)) { print("hello\n"); }</code></pre>
|
||
<p>and this is clearly not standard (in ECMA, Yes you can)
|
||
- accessing properties on literal String or Number is not supported; For
|
||
example this code will not work</p>
|
||
<pre><code> 'abc'.x = 1;</code></pre>
|
||
<p>and this can cause a crash, like in many other unsupported features
|
||
- when statement execution is off (for example inside an else {} statement to be
|
||
NOT executed) the code is partially interpreted to obtain intermediate results
|
||
that will be not used and will be freed by the next GC call. Some memory is
|
||
wasted in the process.
|
||
- recursive object structure like this:</p>
|
||
<pre><code> var obj = {};
|
||
obj.x = 32;
|
||
obj["yy"] = 'hello';
|
||
obj.recursive = obj;</code></pre>
|
||
<p>is not supported and will cause an infinite loop inside GC. Recursive
|
||
structures need to be handled with some kind of data <em>sentinel values</em>.
|
||
- maximum size of input source code is 65536 bytes but you can change the buffer
|
||
size inside main function (change 1<<16 constant)</p>
|
||
<p>Parsing errors are not handled so “garbage in garbage out” (but garbage is also
|
||
collected if no errors are found, ah ah).</p>
|
||
<h4 id="equality">Equality</h4>
|
||
<p>Loose equality is implemented similarly as described in
|
||
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Equality_comparisons_and_sameness#:~:text=is%20logged%0A%7D-,Loose%20equality%20using%20%3D%3D,-Loose%20equality%20is">this docs</a>
|
||
From that doc, the following paragraph is slightly different (point 2 is
|
||
missing becouse some of the required features are not present in the
|
||
interpreter):</p>
|
||
<p>Loose equality is symmetric: A == B always has identical semantics to B == A
|
||
for any values of A and B (except for the order of applied conversions). The
|
||
behavior for performing loose equality using == is as follows:</p>
|
||
<ol type="1">
|
||
<li>If the operands have the same type, they are compared as follows:</li>
|
||
</ol>
|
||
<ul>
|
||
<li>Object: return 1 only if both operands reference the same object.</li>
|
||
<li>String: return 1 only if both operands have the same characters in the same
|
||
order.</li>
|
||
<li>Number: return 1 only if both operands have the same value. +0 and -0 are
|
||
treated as the same value. NaN is not supported</li>
|
||
</ul>
|
||
<ol start="3" type="1">
|
||
<li>If one of the operands is an object and the other is a primitive, convert
|
||
the object to a primitive.</li>
|
||
<li>At this step, both operands are converted to primitives (one of String and
|
||
Number. The rest of the conversion is done case-by-case.</li>
|
||
</ol>
|
||
<ul>
|
||
<li>If they are of the same type, compare them using step 1.</li>
|
||
<li>Number to String: convert the string to a number. Conversion failure
|
||
results in 0 (not NaN), which will NOT guarantee the equality to be false.</li>
|
||
</ul>
|
||
<p>This logic has been implemented in this (light) obfuscated code:</p>
|
||
<pre><code> if (_ == y->m) {
|
||
if (_ & 4) U = x != y;
|
||
else if (_ & 1) goto O;
|
||
else goto Q;
|
||
} else if ((_ | y->m) == 2) {
|
||
Q: U = A(Q(x)->f, Q(y)->f);
|
||
} else {
|
||
O: U = V - X;
|
||
}
|
||
// U == 0 mean true loose equality</code></pre>
|
||
<p><em>NOTE: the (light) obfuscated code can be slightly different from the final
|
||
one, due to final refinements on the obfuscation process.</em></p>
|
||
<h4 id="garbage-collection">Garbage Collection</h4>
|
||
<p>Garbage Collection is implemented with these two functions (in pseudocode):</p>
|
||
<pre><code> // x=item z=terminal y=mark_value
|
||
mark(x, z, y, next)
|
||
if x and x != z
|
||
if value(x) not already marked
|
||
mark(value(x), 0, y, 0)
|
||
mark(props(value(x)), 0, y, 1)
|
||
mark(props(x), 0, y, 1)
|
||
mark x = y
|
||
if next
|
||
mark(next(x), z, y, 1)
|
||
|
||
// z=terminal, stop when z is reached
|
||
gc(z)
|
||
// all values from head to z are marked to 1
|
||
mark(all, z, 1, 1)
|
||
// all values connected to props are marked to 0
|
||
mark(props, 0, 0, 1)
|
||
foreach x in all
|
||
if x marked and x not retained
|
||
if type(x) == String
|
||
freeString(x)
|
||
free(x)
|
||
else
|
||
mark x = 0</code></pre>
|
||
<p>It is <em>clear and quite obvious</em> that this is a classical <strong>mark & sweep</strong>
|
||
process that marks all the local values (from a given heap starting point) and
|
||
release all values that are not referenced from any heap variable (recursively,
|
||
also for any values referenced from infinite nested objects properties).</p>
|
||
<p>Unfortunately, this implementation does not support recursive Object structures
|
||
where the properties tree includes the root node itself or any other parent node.
|
||
I was not able to include this in the compressed program, without exceed the
|
||
tokens limitation.</p>
|
||
<p>The following chart show the length of the two global linked lists (for
|
||
variables and values) while executing the internal tests in C (see <strong>Internal
|
||
tests</strong> chapter):</p>
|
||
<figure>
|
||
<img src="gc.png" alt="Garbage Collector actions inside internal tests" />
|
||
<figcaption aria-hidden="true">Garbage Collector actions inside internal tests</figcaption>
|
||
</figure>
|
||
<h4 id="values-and-variables">Values and variables</h4>
|
||
<p>The interpreter uses the same struct to hold values and variables. The same
|
||
struct (c) is also used to store a linked list for global variables and a linked
|
||
list for all properties contained inside a variable.</p>
|
||
<p>Using the same struct has been useful to strip down the code for a lesser number
|
||
of tokens.</p>
|
||
<p>This is a semi obfuscated struct that holds variables or values (NOTE: obfuscated
|
||
symbols can be slightly different from the final obfuscated ones):</p>
|
||
<pre><code> typedef struct c*d;
|
||
struct c{
|
||
union{
|
||
char*e;
|
||
double n;
|
||
} ;
|
||
char *f, //{id} set if var is a property
|
||
g, // used in gc (mark)
|
||
h, // used in gc (retain)
|
||
l; // type (1 = number, 2 = string, 4 = object)
|
||
d d, // when this is a value, is one of the variables that use this value
|
||
x, // when this is an variable this point to the variable value
|
||
b, // next value or variable in the list (linked list)
|
||
m, // object properties head (linked list of props)
|
||
(*q)(); // native function, used then type is 2 and this is a function
|
||
int r; // array length / properties length
|
||
} ;</code></pre>
|
||
<p>This struct is huge! It uses 72 bytes on 64bit X86 architecture. This struct is
|
||
big to fit all the useful data inside it and also something else that is
|
||
needed to compress the code. In theory can be much smaller but the final code
|
||
will uses more tokens (I have tried multiple times to implement this interpreter
|
||
but this is the only solution that I’ve found to match the 2503 limitation.. with
|
||
the previous limit of 2053 was really hard to stay under the limits – for me!).
|
||
Keep in mind also that on 64bit X86 architecture each pointer cost 8 bytes…</p>
|
||
<p>The interpreter uses a large amount of RAM, creating many temporary values
|
||
objects that are not really necessary but are needed to compress the code.</p>
|
||
<p>Global variables linked list is stored inside a global pointer, and all
|
||
temporary values are stored inside another pointer. Garbage Collector work to
|
||
free a subset of unused values not pointed from current global variables list.</p>
|
||
<h4 id="native-functions">Native functions</h4>
|
||
<p>The only native function included inside the obfuscated program is <strong>print</strong>,
|
||
that prints out the first parameter that receive, as a String (by converting it
|
||
to a String if is not a String type); its native code is inside the bb function:</p>
|
||
<pre><code> e bb(e u) {
|
||
printf("%s", Q(u->x->q->x));
|
||
return E(0);
|
||
}</code></pre>
|
||
<p>This native function is declared in global scope using this code, inside main
|
||
function:</p>
|
||
<pre><code> c(g, "print");
|
||
e x = G(&q);
|
||
x->m = 2;
|
||
x->x->r = bb;</code></pre>
|
||
<p>And the same sequence of calls over c() and G() can be iterated to declare
|
||
additional native functions inside the global context of the interpreter.</p>
|
||
<p>If you want you can declare other native functions. Each native function receive
|
||
a parameter of type <em>e</em> (pointer to struct c) that is the value of the temporary
|
||
<strong>arguments</strong> array. Each native function MUST return a new value using <em>E()</em>
|
||
function otherwise the interpreter will crash.</p>
|
||
<p>I’m sure that will be a little hard to add new native functions writing directly
|
||
them in this (light) obfuscated format, but in theory this is not impossible…</p>
|
||
<p>For example, this is a new <em>keys</em> native function that can returns an array of
|
||
all properties contained inside an object, given the target Object as first
|
||
parameter (like the Object.keys standard method):</p>
|
||
<pre><code> e be(e u) {
|
||
int y = 0; // index for properties declared in output Array
|
||
e h = u->x->q->x->q, // get first argument only
|
||
x = E(4), // declare a new value with type 4 = Object (Array!)
|
||
l;
|
||
// loop for each properties inside arg
|
||
while (h) {
|
||
c(g, "%d", y++); // format N=y for output Array index property
|
||
l = G(&x->q); // declare a new property N=y inside x
|
||
x->u++; // increment number of properties in x
|
||
l->x->m = 2; // type of new property value is 2 = String
|
||
l->x->f = D(h->g); // duplicate ID of the arg Object property
|
||
h = h->b; // jump on the next property of arg Object
|
||
}
|
||
return x;
|
||
}</code></pre>
|
||
<p>These (light) obfuscated functions and variables are used in the previous
|
||
examples:</p>
|
||
<pre><code> c // sprintf alias
|
||
g // char[] array used for F and G functions
|
||
D // strdup
|
||
E(T) // allocate a new value of type T
|
||
F(props) // find property f[] inside props linked list
|
||
G(&props) // declare property f[] inside props linked list</code></pre>
|
||
<p>Also, just for fun, the following <strong>exec</strong> native function can be used to
|
||
expose <strong>popen</strong> inside the interpreter, to exec any arbitrary shell subprocess
|
||
commands:</p>
|
||
<pre><code> e bc(e u) {
|
||
FILE *bd = popen(Q(u->x->q->x)->f, "r");
|
||
e x = E(2);
|
||
x->f = D("");
|
||
while (fgets(g, 99, bd) != 0) {
|
||
char *d = malloc(w(x->f) + w(g) + 1);
|
||
c(d, "%s%s", x->f, g);
|
||
free(x->f);
|
||
x->f = d;
|
||
}
|
||
pclose(bd);
|
||
return x;
|
||
}</code></pre>
|
||
<p>Unfortunately <strong>keys</strong> and <strong>exec</strong> functions were not included in the obfuscated
|
||
<em>prog.c</em> program, due to tokens limitations.</p>
|
||
<h3 id="stranger-things">Strange(r) Things</h3>
|
||
<p>The following things comes from the <em>Upside Down</em>…
|
||
Strange things can happen or can be found when using this interpreter:</p>
|
||
<ul>
|
||
<li>Array are implemented as Object and <strong>length</strong> property of Array values are
|
||
implemented assuming that Array values have a list of properties that are
|
||
numerical indexes and there are not <em>holes</em> inside the list of all indexes.
|
||
For example this array is ok:</li>
|
||
</ul>
|
||
<pre><code> var x = [];
|
||
x[0] = 1;
|
||
x[1] = 'hello';
|
||
x[2] = "From\tUpside\tDown";</code></pre>
|
||
<p>and its length is 3. The following one is instead a NOT-OK Array that contains
|
||
<em>holes</em> inside its own indexes:</p>
|
||
<pre><code> var y = [];
|
||
y[0] = 'I have';
|
||
y[2] = "one hole";</code></pre>
|
||
<p>and its length is computed as 2 instead of 3
|
||
- Object has <strong>length</strong> property and the object length is equal to the number
|
||
of properties declared inside the Object (not standard)
|
||
- functions are implemented as String, so you can do strange(r) things like this:</p>
|
||
<pre><code> var x = "{ print('x'); }";
|
||
x();</code></pre>
|
||
<p>this also mean that you can implement an eval function that can evaluate
|
||
dynamic generated code using this trick:</p>
|
||
<pre><code> var eval = function() {
|
||
arguments[0]();
|
||
}</code></pre>
|
||
<p>this technique can also work if you create a dynamic string (like normally is
|
||
done while using eval). Please note that the given string need to include an
|
||
outer statement ({}) otherwise the execution will be interrupted after the
|
||
first expression or short statement. See <strong>utils.js</strong> or <strong>verify.js</strong> for
|
||
some real examples.
|
||
- 0.1 + 0.2 does compute 0.3 correctly. Node.js returns 0.30000000000000004
|
||
instead
|
||
- <strong>var</strong> can be used multiple times like this:</p>
|
||
<pre><code> var var var var a = 1;</code></pre>
|
||
<p>and this will work like var has been typed one time. Can you find out where
|
||
this has been allowed inside the source code? (hint: goto)
|
||
- many other unknown strange things can happen in Hawkins, Indiana…</p>
|
||
<h3 id="useful-things">Useful Things</h3>
|
||
<p>Console prototype is not implemented but console.log can be implemented using
|
||
only native <strong>print</strong> function. The following is an example of Console
|
||
implementation (found also inside console.js file):</p>
|
||
<pre><code> var console = {};
|
||
console.log = function () {
|
||
var i = 0;
|
||
while (i < arguments.length) {
|
||
var arg = "" + arguments[i];
|
||
print(arg);
|
||
i += 1;
|
||
if (i == arguments.length) {
|
||
break;
|
||
}
|
||
print(' ');
|
||
}
|
||
print('\n');
|
||
};</code></pre>
|
||
<p>You can use this with cat when testing the interpreter:</p>
|
||
<pre><code> cat console.js fibo.js | ./prog</code></pre>
|
||
<p>Other standard functions can be implement slightly different from the ECMA
|
||
standard library original ones but still something useful can be achieved:</p>
|
||
<pre><code> var String = {};
|
||
|
||
// slice(string, from, to)
|
||
String.slice = function () {
|
||
if (arguments.length < 2) {
|
||
return "";
|
||
}
|
||
var s = arguments[0];
|
||
var from = arguments[1];
|
||
var to = -1;
|
||
if (arguments.length > 2) {
|
||
to = arguments[2];
|
||
} else {
|
||
to = s.length;
|
||
}
|
||
var o = "";
|
||
var i = from;
|
||
while (i < to) {
|
||
o += s[i];
|
||
i += 1;
|
||
}
|
||
return o;
|
||
}</code></pre>
|
||
<p>see <em>string.js</em> for additional String methods implemented using only the few
|
||
features present in this little mini voodoo interpreter.</p>
|
||
<p>If you need both <strong>Console</strong> and <strong>String</strong> utils, use cat:</p>
|
||
<pre><code> cat console.js string.js YOUR_SCRIPT.js | ./prog</code></pre>
|
||
<p>Where YOUR_SCRIPT.js is some script that you can create, not included in this
|
||
package. If you want to try out some other scripts bundled with this package,
|
||
see <strong>Run Forrest, run</strong> chapter.</p>
|
||
<p>The following list includes a summary of all of my examples for standard JS
|
||
features implemented using this interpreter. These methods are not fully
|
||
implemented and each method work slightly differently from the one that exists
|
||
inside the ECMA standard:</p>
|
||
<pre><code> // string.js
|
||
String.slice(s, start, end);
|
||
String.split(s, singleCharSeparator);
|
||
String.charAt(s, index);
|
||
String.charCodeAt(s, index);
|
||
String.startsWith(s, prefix);
|
||
String.fromCharCode(asciiCode);
|
||
String.trimStart(s);
|
||
String.trimEnd(s);
|
||
String.trim(s);
|
||
|
||
// array.js
|
||
Array.at(array, index);
|
||
Array.join(array, separator);
|
||
Array.push(array, item1 ... itemN);
|
||
Array.pop(array); // return { new: NEW_ARRAY, removed: REMOVED_ITEM }
|
||
Array.concat(array1, array2);
|
||
Array.slice(array, start, end);
|
||
Array.toString(array);
|
||
Array.filter(array, callback);
|
||
|
||
// math.js
|
||
Math.E;
|
||
Math.e;
|
||
Math.LN2;
|
||
Math.LN10;
|
||
Math.LOG2E;
|
||
Math.LOG10E;
|
||
Math.phi;
|
||
Math.PI;
|
||
Math.pi;
|
||
Math.SQRT1_2;
|
||
Math.SQRT2;
|
||
Math.tau;
|
||
Math.sign(x);
|
||
Math.abs(x);
|
||
|
||
// console.js
|
||
console.log(...);
|
||
|
||
// utils.js
|
||
eval(string);</code></pre>
|
||
<p><em>NOTE: each file includes some unit tests that can be enabled manually inside
|
||
each file by setting a test variable equal to 1 or by prepending the test.js
|
||
script before the source file (see <strong>Run Forrest, run</strong> chapter)</em></p>
|
||
<p><em>NOTE(2): take a look at Array.filter! The interpreter support function callbacks</em></p>
|
||
<h3 id="internal-tests">Internal tests</h3>
|
||
<p>The following tests has been made in the original not-obfuscated code development.
|
||
Without these tests, the implementation of the interpreter would have been
|
||
impossible or at least exhausting.</p>
|
||
<p><em>NOTE: some tests uses additional native functions like exec and keys that has
|
||
been stripped down in the final (light) obfuscated version of the code</em></p>
|
||
<h4 id="tests-functions">Tests functions</h4>
|
||
<pre><code> /**
|
||
* Test a single JS Expression evaluation.
|
||
* return value MUST be equal to type
|
||
* return value, if type=1 (Number) MUST be equal to res
|
||
* return value, if type=2 (String) MUST be equal to sres
|
||
*/
|
||
void etest_base(char *src, char type, double res, char *sres);
|
||
|
||
// same as etest_base but assume that return type MUST be 1 (Number)
|
||
void etest(char *src, double res);
|
||
/**
|
||
* Test a JS Statement
|
||
* return value MUST be equal to type
|
||
* return value, if type=1 (Number) MUST be equal to res
|
||
* return value, if type=2 (String) MUST be equal to sres
|
||
*/
|
||
void stest_base(char *src, char type, double res, char *sres);
|
||
|
||
// same as stest_base but assume that return type MUST be 1 (Number)
|
||
void stest(char *src, double res);</code></pre>
|
||
<h4 id="verify-single-expressions-correctness">Verify single Expressions correctness</h4>
|
||
<pre><code> spf(id, "x"); decl(&props); // declare x variable
|
||
etest_base(";", 0, 0, 0);
|
||
etest_base("; 2; 3", 0, 0, 0);
|
||
etest("2*(2+3)-1", 9);
|
||
etest_base("'' + 32", 2, 0, "32");
|
||
etest_base("\"\" + 32", 2, 0, "32");
|
||
etest("x + 32", 32);
|
||
etest("x = 3", 3);
|
||
etest("x", 3);
|
||
etest("var y = 11", 11);
|
||
etest("var y = 11, z = 22", 22);
|
||
etest_base("var foo = function () { 2; }", 2, 0, "{ 2; }");
|
||
etest_base("' 1 2 \\n 23 \\' '", 2, 0, " 1 2 \n 23 \' ");
|
||
etest(" 'hello' == 'hello'; ", 1);
|
||
etest(" 'hello' == 'hello2'; ", 0);
|
||
etest(" 'hello' != 'hello2'; ", 1);
|
||
etest(" '32' == 32; ", 1);
|
||
etest(" '32' == 33; ", 0);
|
||
etest(" '32' <= 33; ", 1);
|
||
etest(" '32' <= 2; ", 0);
|
||
etest(" '2' >= 32; ", 0);
|
||
etest(" '32' >= 2; ", 1);
|
||
etest(" 32 == 32; ", 1);
|
||
etest(" 32 >= 0; ", 1);
|
||
etest(" 0 <= 32; ", 1);
|
||
etest(" 1+4 && 3-3; ", 0);
|
||
etest(" 1+4 || 3-3; ", 1);
|
||
etest(" 1<<3; ", 8);
|
||
etest(" 8>>3; ", 1);
|
||
etest(" -6; ", -6);
|
||
etest(" +'6'; ", 6);
|
||
etest(" ~'6'; ", -7);
|
||
etest(" !'6'; ", 0);
|
||
etest(" !'0'; ", 1);
|
||
etest(" !(1 && 2); ", 0);
|
||
etest(" 0.1 + 0.2 == 0.3; ", 1); // OMG
|
||
etest(" 'hello'.length; ", 5);
|
||
etest(" [].length; ", 0);
|
||
etest(" {}.length; ", 0); // Stranger Things in Hawkins, Indiana
|
||
etest_base("obj = {}", 4, 0, 0);
|
||
etest("obj.abc = 32", 32);
|
||
etest("obj[0] = 2; ", 2);
|
||
etest("obj[0]; ", 2);
|
||
etest_base("obj[1] = {}; ", 4, 0, 0);
|
||
etest("obj[1].temp = 123; ", 123);
|
||
etest("obj[1].temp; ", 123);
|
||
etest_base("print(1); ", 0, 0, 0);
|
||
etest_base("print(3*9); ", 0, 0, 0);
|
||
etest_base("print('22'); ", 0, 0, 0);
|
||
etest_base("'abc'[0];", 2, 0, "a");
|
||
etest_base("'1+2'();", 1, 3, 0);
|
||
etest("0 || 'hello'", 0);
|
||
etest("0 || '1hello'", 1);
|
||
etest_base("print(123);", 0, 0, 0);</code></pre>
|
||
<h4 id="verify-statement-correctness">Verify Statement correctness</h4>
|
||
<pre><code> stest("; 2; 3", 3);
|
||
stest("{ { 1; }; }; 2; {{3;}}", 3);
|
||
stest("{ { return 1; } } 2; 3", 1);
|
||
stest_base("var strt = '' + 32", 2, 0, "32");
|
||
stest_base("var strt = \"\" + 32", 2, 0, "32");
|
||
stest("var foo = function () { var w = 2 * 3; }; foo(1, 2, 3)", 6);
|
||
stest("var foo = function () { \
|
||
arguments[1] * arguments[2] * arguments[0]; \
|
||
}; foo(10, 2, 3)", 60);
|
||
stest("var x = 2 + 3, y; \
|
||
if (x == 5) { \
|
||
7; \
|
||
} else { \
|
||
2; \
|
||
}", 7);
|
||
stest("var x = 2 + 3, y; \
|
||
if (x == 3) { \
|
||
7; \
|
||
} else { \
|
||
2; \
|
||
}", 2);
|
||
stest("var x = 2 + 3, y; \
|
||
if (x == 5) \
|
||
7; \
|
||
else \
|
||
2; \
|
||
", 7);
|
||
stest("var x = 2 + 3, y; \
|
||
if (x == 3) \
|
||
7; \
|
||
else \
|
||
2; \
|
||
", 2);
|
||
stest("var x = 2 + 3, y; \
|
||
if (x == 5) \
|
||
y = 7; \
|
||
else \
|
||
y = 3; \
|
||
y;", 7);
|
||
stest("var x = 2 + 3, y; \
|
||
if (x == 3) \
|
||
y = 7; \
|
||
else \
|
||
y = 3; \
|
||
y;", 3);
|
||
stest("var cnt = 0; \
|
||
while (cnt < 10) { cnt = cnt + 1; break; } \
|
||
cnt", 1);
|
||
stest("var cnt = 0; \
|
||
while (cnt < 10) { if (1) { break; } cnt = cnt + 1; } \
|
||
cnt", 0);
|
||
stest("var cnt = 0, x = 0; \
|
||
while (x < 4) { \
|
||
var y = 0; \
|
||
while (y < 4) { \
|
||
if ((y == 2) && (x == 2)) \
|
||
break; \
|
||
cnt = cnt + 1; \
|
||
y += 1; \
|
||
} \
|
||
if ((y == 2) && (x == 2)) \
|
||
break; \
|
||
x += 1; \
|
||
}\
|
||
cnt", 10);
|
||
stest("var cnt = 0; while (cnt < 10) { cnt = cnt + 1; } cnt", 10);
|
||
stest("var o2 = {}, l2 = 1; o2.l2 = 33; o2.l2", 33);
|
||
stest("var o2 = {}, l2 = 1; o2.l2 = 31; l2", 1);
|
||
stest("var w2 = 1; w2 += 3", 4);
|
||
stest("var true = 1, false = 0; 1 == true && !1 == false", 1);
|
||
stest("var s = 1; ~s; s", 1);
|
||
stest_base("var s = '1'; ~s; s", 2, 0, "1");
|
||
stest(" \n\
|
||
var number = 10, \n\
|
||
n1 = 0, \n\
|
||
n2 = 1, \n\
|
||
i = 0, \n\
|
||
nextTerm; \n\
|
||
var console = {}; \n\
|
||
console.log = print; \n\
|
||
console.log('fibonacci series:'); \n\
|
||
while (i <= number) { \n\
|
||
console.log(n1); \n\
|
||
nextTerm = n1 + n2; \n\
|
||
n1 = n2; \n\
|
||
n2 = nextTerm; \n\
|
||
i += 1; \n\
|
||
} \n\
|
||
n1; \n\
|
||
", 89);
|
||
stest(" \n\
|
||
var x = 32, y = 10; \n\
|
||
var console = {}; \n\
|
||
console.log = function () { \n\
|
||
var i = 0; \n\
|
||
while (i < arguments.length) { \n\
|
||
var arg = \"\" + arguments[i]; \n\
|
||
print(arg); \n\
|
||
i += 1; \n\
|
||
if (i == arguments.length) { \n\
|
||
break; \n\
|
||
} \n\
|
||
print(' '); \n\
|
||
} \n\
|
||
print('\\n'); \n\
|
||
}; \n\
|
||
console.log('test 123', {}, x+y, []); \n\
|
||
0; \n\
|
||
", 0);
|
||
stest(" \n\
|
||
var x = {}; \n\
|
||
x.a = 32; \n\
|
||
x.b = 1; \n\
|
||
x.prova = \"abc\"; \n\
|
||
var y = keys(x); \n\
|
||
var z = y.length; \n\
|
||
while (z) { \n\
|
||
z -= 1; \n\
|
||
print(y[z]); \n\
|
||
} \n\
|
||
0;", 0);
|
||
stest("var null = {}; var nullx = null; null == nullx;", 1);
|
||
stest("var null = {}; null == {};", 0);
|
||
stest("var null = {}; null == 0;", 1);
|
||
stest("var undefined = {}; undefined == 0;", 1);
|
||
stest("var undefined = {}, null = {}; undefined == null;", 0);
|
||
stest_base("exec('echo -n 123');", 2, 0, "123");
|
||
stest(" \n\
|
||
var recobj = {}; \n\
|
||
{ \n\
|
||
var o1 = {}; \n\
|
||
var o2 = {}; \n\
|
||
recobj.x = o1; \n\
|
||
recobj.y = o2; \n\
|
||
o1.x = 3; \n\
|
||
o2.o1 = o1; \n\
|
||
o1 = 0; \n\
|
||
o2 = 0; \n\
|
||
} \n\
|
||
recobj.y.o1.x;", 3);
|
||
stest_base(" \n\
|
||
var String = {}; \n\
|
||
// slice(string, from, to) \n\
|
||
String.slice = function () { \n\
|
||
if (arguments.length < 2) { \n\
|
||
return ''; \n\
|
||
} \n\
|
||
var s = arguments[0]; \n\
|
||
var from = arguments[1]; \n\
|
||
var to; \n\
|
||
if (arguments.length > 2) { \n\
|
||
to = arguments[2]; \n\
|
||
} else { \n\
|
||
to = s.length; \n\
|
||
} \n\
|
||
var o = ''; \n\
|
||
var i = from; \n\
|
||
while (i < to) { \n\
|
||
o += s[i]; \n\
|
||
i += 1; \n\
|
||
} \n\
|
||
return o; \n\
|
||
}; \n\
|
||
var test = 'hello world!'; \n\
|
||
//print(String.slice(test, 1) + '\\n'); \n\
|
||
print(String.slice(test, 1, 5) + '\\n');\n\
|
||
String.slice(test, 1, 5);", 2, 0, "ello");
|
||
stest_base(" \n\
|
||
// split(string, singleCharSeparator) \n\
|
||
var String = {}; \n\
|
||
String.split = function () { \n\
|
||
var o = []; \n\
|
||
var s = arguments[0]; \n\
|
||
var delimiter = arguments[1]; \n\
|
||
var start = 0; \n\
|
||
var i = 0; \n\
|
||
var oindex = 0; \n\
|
||
while (i < s.length) { \n\
|
||
if (s[i] == delimiter) { \n\
|
||
o[oindex] = String.slice(s, start, i); \n\
|
||
start = i + 1; \n\
|
||
oindex += 1; \n\
|
||
} \n\
|
||
i += 1; \n\
|
||
} \n\
|
||
if (start < s.length) { \n\
|
||
o[oindex] = String.slice(s, start); \n\
|
||
} \n\
|
||
return o; \n\
|
||
}; \n\
|
||
// slice(string, from, to) \n\
|
||
String.slice = function () { \n\
|
||
if (arguments.length < 2) { \n\
|
||
return ''; \n\
|
||
} \n\
|
||
var s = arguments[0]; \n\
|
||
var from = arguments[1]; \n\
|
||
var to; \n\
|
||
if (arguments.length > 2) { \n\
|
||
to = arguments[2]; \n\
|
||
} else { \n\
|
||
to = s.length; \n\
|
||
} \n\
|
||
var o = ''; \n\
|
||
var i = from; \n\
|
||
while (i < to) { \n\
|
||
o += s[i]; \n\
|
||
i += 1; \n\
|
||
} \n\
|
||
return o; \n\
|
||
}; \n\
|
||
var test2 = 'abc, d, e f'; \n\
|
||
var splitted = String.split(test2, ','); \n\
|
||
print(splitted.length + '\n'); \n\
|
||
print(splitted[0] + '\n'); \n\
|
||
print(splitted[1] + '\n'); \n\
|
||
print(splitted[2] + '\n'); \n\
|
||
splitted[0];", 2, 0, "abc");
|
||
stest(" \n\
|
||
var _cm = {}; // map from character to ASCII code \n\
|
||
_cm['\\x0'] = 0; _cm['\\x1'] = 1; _cm['\\x2'] = 2; \n\
|
||
_cm['\\x3'] = 3; _cm['\\x4'] = 4; _cm['\\x5'] = 5; \n\
|
||
_cm['\\x6'] = 6; _cm['\\x7'] = 7; _cm['\\x8'] = 8; \n\
|
||
_cm['\\x9'] = 9; _cm['\\xa'] = 10; _cm['\\xb'] = 11; \n\
|
||
_cm['\\xc'] = 12; _cm['\\xd'] = 13; _cm['\\xe'] = 14; \n\
|
||
_cm['\\xf'] = 15; _cm['\\x10'] = 16; _cm['\\x11'] = 17; \n\
|
||
_cm['\\x12'] = 18; _cm['\\x13'] = 19; _cm['\\x14'] = 20; \n\
|
||
_cm['\\x15'] = 21; _cm['\\x16'] = 22; _cm['\\x17'] = 23; \n\
|
||
_cm['\\x18'] = 24; _cm['\\x19'] = 25; _cm['\\x1a'] = 26; \n\
|
||
_cm.length", 27);
|
||
stest_base(" \n\
|
||
var stest = function () { return arguments[0] + '_1'; }; \n\
|
||
var s = 'abc'; \n\
|
||
s = stest(s); \n\
|
||
s = stest(s); \n\
|
||
s;", 2, 0, "abc_1_1");
|
||
stest(" \
|
||
var test = function () { \
|
||
var x = arguments[0]; \
|
||
if (x < 0) \
|
||
return 2; \
|
||
return 3; \
|
||
}; \
|
||
var t1 = test(-32); \
|
||
t1;", 2);
|
||
stest_base(" \n\
|
||
var stest = function () { \n\
|
||
var o = arguments[0] + '_'; \n\
|
||
var i = 0; \n\
|
||
while (i < 2) { \n\
|
||
o += i; \n\
|
||
i += 1; \n\
|
||
} \n\
|
||
return o; \n\
|
||
}; \n\
|
||
var s = 'abc'; \n\
|
||
s = stest(s); \n\
|
||
s = stest(s); \n\
|
||
s;", 2, 0, "abc_01_01");
|
||
stest_base(" \
|
||
var test = function () { \
|
||
var x = arguments[0]; \
|
||
var o = 'x'; \
|
||
if (x > 0) { \
|
||
o += 2; \
|
||
} else { \
|
||
o += 3; \
|
||
} \
|
||
return o; \
|
||
}; \
|
||
var t1 = test(-32); \
|
||
t1;", 2, 0, "x3");
|
||
stest(" \
|
||
var atmp = []; \
|
||
atmp[0] = 10; \
|
||
atmp[1] = 20; \
|
||
atmp[0 + 1];", 20);</code></pre>
|
||
<p>Some of these tests has also been ported in plain JavaScript; take a look inside
|
||
the <strong>verify.js</strong> script. These tests uses <strong>eval()</strong> function to execute a test
|
||
source code string and verify its execution results. These other tests can be
|
||
executed with this command:</p>
|
||
<pre><code> cat utils.js verify.js | ./prog</code></pre>
|
||
<h3 id="f.a.q">F.A.Q</h3>
|
||
<p>These are frequently asked questions that can be asked when trying to use this
|
||
little mambo jumbo interpreter:</p>
|
||
<ul>
|
||
<li>why? because why not</li>
|
||
<li>if program crash, check if you are calling some function that is not declared
|
||
or has been misspelled. There are no safety check about this</li>
|
||
<li>if program loop indefinitely, check if your function assignments includes a
|
||
final ; after }. This is required</li>
|
||
<li>what is the meaning of life and everything? 42 or sometimes 2503</li>
|
||
<li>if program do not do what you want, check the supported features because the
|
||
parser can easily misunderstand what you are giving as input text; for example
|
||
you can type <strong>let</strong> instead of <strong>var</strong> and, without any errors raised,
|
||
everything will go terribly wrong</li>
|
||
<li>can this interpreter be used on real production environment? of course not</li>
|
||
<li>what is the memory limit of this interpreter? there is no limits</li>
|
||
<li>what happen if an object contains a reference to itself (object with a
|
||
recursive structure)? An infinite loop is triggered because the Garbage
|
||
Collector cannot recognize this kind of structure</li>
|
||
</ul>
|
||
<h3 id="final-thoughts">Final Thoughts</h3>
|
||
<p>I’ve tried multiple time to rewrite this interpreter and compress the final
|
||
obfuscated source code to match the previous IOCCC limit but without success.</p>
|
||
<p>The increased limitation of tokens from 2053 to 2503 has made possible to use
|
||
this iteration of the interpreter without continue to retry to rewrite that to
|
||
compress the final source code. Probably with more iterations the 2053 limit
|
||
should be reached, who knows.</p>
|
||
<p>The most tricky part in the development process has been to find out a minimal
|
||
bare bones subset of JavaScript features, small enough to allow to build some
|
||
other standard features on top of the bare bones interpreter.</p>
|
||
<p>In many tests I’ve tried to include the Object.keys features (to obtain the
|
||
list of property keys inside a given Object) but, in all of my attempts, the
|
||
limitation on tokens count was exceeded. With Object.keys would have been
|
||
possible to build up also Object.values and other Object methods. Also the lack
|
||
of prefix/postfix ++ and – it’s a pity… Damn it!
|
||
May be without the Garbage Collector some other features should be packed into
|
||
the program but it’s too late to change that.</p>
|
||
<p>Have fun!</p>
|
||
<!--
|
||
|
||
Copyright © 1984-2025 by Landon Curt Noll and Leonid A. Broukhis. All Rights Reserved.
|
||
|
||
You are free to share and adapt this file under the terms of this license:
|
||
|
||
Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)
|
||
|
||
For more information, see:
|
||
|
||
https://creativecommons.org/licenses/by-sa/4.0/
|
||
|
||
-->
|
||
<!-- AFTER: last line of markdown file: 2024/maffiodo/README.md -->
|
||
|
||
<!-- END: this line ends content for HTML phase 21 by: bin/pandoc-wrapper.sh via bin/md2html.sh -->
|
||
<!-- START: this line starts content for HTML phase 22 by: bin/output-index-inventory.sh via bin/md2html.sh -->
|
||
|
||
<!-- START: this line starts content generated by: bin/output-index-inventory.sh -->
|
||
|
||
<div id="inventory">
|
||
<h1 id="inventory-for-2024maffiodo">Inventory for 2024/maffiodo</h1>
|
||
</div>
|
||
<h2 id="primary-files">Primary files</h2>
|
||
<ul>
|
||
<li><a href="https://github.com/ioccc-src/winner/blob/master/2024/maffiodo/prog.c">prog.c</a> - entry source code</li>
|
||
<li><a href="https://github.com/ioccc-src/winner/blob/master/2024/maffiodo/Makefile">Makefile</a> - entry Makefile</li>
|
||
<li><a href="https://github.com/ioccc-src/winner/blob/master/2024/maffiodo/prog.orig.c">prog.orig.c</a> - original source code</li>
|
||
<li><a href="https://github.com/ioccc-src/winner/blob/master/2024/maffiodo/array.js">array.js</a> - javascript array test</li>
|
||
<li><a href="https://github.com/ioccc-src/winner/blob/master/2024/maffiodo/console.js">console.js</a> - javascript console implementation</li>
|
||
<li><a href="https://github.com/ioccc-src/winner/blob/master/2024/maffiodo/fibo.js">fibo.js</a> - javascript fibonacci test</li>
|
||
<li><a href="https://github.com/ioccc-src/winner/blob/master/2024/maffiodo/gc.png">gc.png</a> - garbage Collector actions inside internal tests</li>
|
||
<li><a href="https://github.com/ioccc-src/winner/blob/master/2024/maffiodo/math.js">math.js</a> - javascript math test</li>
|
||
<li><a href="https://github.com/ioccc-src/winner/blob/master/2024/maffiodo/string.js">string.js</a> - javascript forrest test</li>
|
||
<li><a href="https://github.com/ioccc-src/winner/blob/master/2024/maffiodo/test.js">test.js</a> - javascript test setup</li>
|
||
<li><a href="https://github.com/ioccc-src/winner/blob/master/2024/maffiodo/try.sh">try.sh</a> - script to try entry</li>
|
||
<li><a href="https://github.com/ioccc-src/winner/blob/master/2024/maffiodo/utils.js">utils.js</a> - javascript utility test</li>
|
||
<li><a href="https://github.com/ioccc-src/winner/blob/master/2024/maffiodo/verify.js">verify.js</a> - verify javascript test execution</li>
|
||
</ul>
|
||
<h2 id="secondary-files">Secondary files</h2>
|
||
<ul>
|
||
<li><a href="2024_maffiodo.tar.bz2">2024_maffiodo.tar.bz2</a> - download entry tarball</li>
|
||
<li><a href="https://github.com/ioccc-src/winner/blob/master/2024/maffiodo/README.md">README.md</a> - markdown source for this web page</li>
|
||
<li><a href="https://github.com/ioccc-src/winner/blob/master/2024/maffiodo/.entry.json">.entry.json</a> - entry summary and manifest in JSON</li>
|
||
<li><a href="https://github.com/ioccc-src/winner/blob/master/2024/maffiodo/.gitignore">.gitignore</a> - list of files that should not be committed under git</li>
|
||
<li><a href=".path">.path</a> - directory path from top level directory</li>
|
||
<li><a href="index.html">index.html</a> - this web page</li>
|
||
</ul>
|
||
<hr style="width:10%;text-align:left;margin-left:0">
|
||
<h4>
|
||
Jump to: <a href="#">top</a>
|
||
</h4>
|
||
|
||
<!-- END: next line ends content generated by: bin/output-index-inventory.sh -->
|
||
|
||
<!-- END: this line ends content for HTML phase 22 by: bin/output-index-inventory.sh via bin/md2html.sh -->
|
||
<!-- START: this line starts content from: inc/after-content.default.html -->
|
||
|
||
</div>
|
||
|
||
<!-- END: this line ends content from: inc/after-content.default.html -->
|
||
<!-- START: this line starts content from: inc/footer.inventory.html -->
|
||
|
||
<!--
|
||
|
||
Copyright © 1984-2025 by Landon Curt Noll. All Rights Reserved.
|
||
|
||
You are free to share and adapt this file under the terms of this license:
|
||
|
||
Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)
|
||
|
||
For more information, see:
|
||
|
||
https://creativecommons.org/licenses/by-sa/4.0/
|
||
|
||
-->
|
||
|
||
<div class="footer">
|
||
|
||
<div id="copyright"><h3>Copyright © 1984-2025 by Landon Curt Noll:
|
||
<a href="https://creativecommons.org/faq/#what-does-some-rights-reserved-mean"
|
||
target="_blank"
|
||
rel="license noopener noreferrer">Some Rights Reserved</a></h3>
|
||
<p>
|
||
This work is <b>licensed by Landon Curt Noll</b> under
|
||
<b><a href="https://creativecommons.org/licenses/by-sa/4.0/?ref=chooser-v1"
|
||
target="_blank"
|
||
rel="license noopener noreferrer"
|
||
style="display:inline-block;">CC BY-SA 4.0</a></b>.
|
||
<img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;"
|
||
src="../../png/cc.png"
|
||
alt="cc inside circle symbol">
|
||
<img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;"
|
||
src="../../png/by.png"
|
||
alt="person inside circle symbol">
|
||
<img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;"
|
||
src="../../png/sa.png"
|
||
alt="arrow looping back on itself inside circle symbol"><br>
|
||
You should <b>carefully review</b> the
|
||
<b><a href="https://creativecommons.org/licenses/by-sa/4.0/legalcode.en"
|
||
target="_blank"
|
||
rel="license noopener noreferrer">CC BY-SA 4.0 LEGAL CODE</a></b>
|
||
before using the licensed material.<br>
|
||
You may wish to review the
|
||
<b><a href="../../license.html"
|
||
target="_blank"
|
||
rel="license noopener noreferrer">highlights of some of the key features and terms</a></b>
|
||
of <b>CC BY-SA 4.0</b>.<br>
|
||
</p>
|
||
</div>
|
||
|
||
<div id="coda"><h3>Coda</h3>
|
||
<p>
|
||
<a href="https://validator.w3.org/nu/?doc=#" rel="nofollow">Nu HTML check this web page</a><br>
|
||
<a href="#top">Jump to top</a> <a href="#content">Jump to Content</a> <a href="#inventory">Jump to Inventory</a><br>
|
||
</p>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<!-- END: this line ends content from: inc/footer.inventory.html -->
|
||
<!-- START: this line starts content from: inc/bottom.default.html -->
|
||
|
||
</body>
|
||
</html>
|
||
|
||
<!-- END: this line ends content from: inc/bottom.default.html -->
|